leerness 1.36.300 → 1.36.301

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/scripts/e2e.js CHANGED
@@ -11,6 +11,14 @@ const { spawnNpmSync } = require('../lib/npm-process');
11
11
  const { gitSpawn } = require('../lib/git');
12
12
  const { EXTERNAL_AGENTS } = require('../lib/agent-registry');
13
13
  const { withRedirectWarnings } = require('./runtime-warning-gate');
14
+ // T-0359: CI runs this file in parallel shards (LEERNESS_E2E_SHARD=i/k, scripts/ci-shard.js). Each top-level case is
15
+ // `if (e2eShard.take('<unique key>')) {` with its `total++;` inside; a shard runs the cases assigned to it and an
16
+ // unsharded run runs all of them. The check next to the E2E result fails the run when a top-level block has no
17
+ // guard, a key repeats, or a guard was not reached exactly once. Case weights: scripts/ci-shard-weights.json.
18
+ // The names below are top-level state that one case leaves for the next: the shared fixture project `tmp`, `run()`
19
+ // (which works in it), and files in it. Every case that mentions one runs in one shard, in this file's order.
20
+ // A new top-level variable that cases change or read across cases belongs in this list.
21
+ const e2eShard = require('./ci-shard').e2eShard(__filename, ['tmp', 'run', 'dsPath', 'dsText', 'secretFile']);
14
22
 
15
23
  // 1.9.12: e2e 안정성을 위해 자식 프로세스의 npm 호출 차단 (hang 방지)
16
24
  // 1.36.87: 일부 e2e 타임아웃을 상향했다 — 격리 실측(agents list 5.2s · init --language 8.5s) 대비
@@ -178,8 +186,11 @@ run('lazy detect', ['lazy', 'detect', tmp]);
178
186
  run('memory search', ['memory', 'search', '마일스톤', '--path', tmp]);
179
187
 
180
188
  const offline = Object.assign({}, process.env, { LEERNESS_OFFLINE: '1' });
181
- total++;
182
- {
189
+ // e2e-shard: cases start here. Below this line, top-level code outside a case only prepares later cases: a case
190
+ // counter (total++), a block or a control-flow block there fails the run (scripts/ci-shard.js). Smoke checks that
191
+ // every shard runs belong above it.
192
+ if (e2eShard.take('update --check (offline): 조회 실패를')) {
193
+ total++;
183
194
  // 1.36.102: 이 단언이 결함을 고정하고 있었다 — 오프라인(레지스트리 조회 불가)에서 "up to date" 가 나오는 것을
184
195
  // 정답으로 못박고 있었다. 모르는 것을 안다고 말하는 게 정답일 수 없다. 계약을 바꾼다:
185
196
  // 조회에 실패하면 '확인 불가' 여야 하고, 마이그레이션이 필요하면 그건 조회와 무관하니 그대로 인정한다.
@@ -191,8 +202,8 @@ total++;
191
202
  }
192
203
 
193
204
  run('auto-update install', ['auto-update', 'install', tmp]);
194
- total++;
195
- {
205
+ if (e2eShard.take('SessionStart hook for update --check ins')) {
206
+ total++;
196
207
  const settingsFile = path.join(tmp, '.claude/settings.local.json');
197
208
  if (fs.existsSync(settingsFile)) {
198
209
  const s = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
@@ -203,8 +214,8 @@ total++;
203
214
  }
204
215
 
205
216
  // 1.9.1 P6 회귀: evidence가 plan:M-XXXX + 검증 키워드면 lazy detect가 통과해야 한다.
206
- total++;
207
- {
217
+ if (e2eShard.take('B(P6)')) {
218
+ total++;
208
219
  const trackerPath = path.join(tmp, '.leerness/progress-tracker.md');
209
220
  let cur = fs.readFileSync(trackerPath, 'utf8');
210
221
  cur = cur.replace(/^\| T-0001 \|.*$/m, '| T-0001 | done | mile A | tests:32/32 (plan:M-0002) | next | 2026-05-08 |');
@@ -215,8 +226,8 @@ total++;
215
226
  if (!ok) failed++;
216
227
  }
217
228
  // 1.9.1 P6 negative: plan:M-XXXX 단독은 여전히 경고
218
- total++;
219
- {
229
+ if (e2eShard.take('B(P6 neg)')) {
230
+ total++;
220
231
  const trackerPath = path.join(tmp, '.leerness/progress-tracker.md');
221
232
  let cur = fs.readFileSync(trackerPath, 'utf8');
222
233
  cur = cur.replace(/^\| T-0001 \|.*$/m, '| T-0001 | done | mile A | plan:M-0002 | next | 2026-05-08 |');
@@ -231,8 +242,8 @@ total++;
231
242
  }
232
243
 
233
244
  // 1.9.1 P1 회귀: legacy leerness-plus hook이 있으면 auto-update install이 정리해야 한다.
234
- total++;
235
- {
245
+ if (e2eShard.take('B(P1)')) {
246
+ total++;
236
247
  const settingsFile = path.join(tmp, '.claude/settings.local.json');
237
248
  const s = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
238
249
  s.hooks.SessionStart = [{ matcher: '*', command: 'leerness-plus update --check' }];
@@ -247,8 +258,8 @@ total++;
247
258
  }
248
259
 
249
260
  // 1.9.1 P4 회귀: NA 마커가 있으면 audit이 placeholder 경고를 스킵.
250
- total++;
251
- {
261
+ if (e2eShard.take('B(P4)')) {
262
+ total++;
252
263
  const ds = path.join(tmp, '.leerness/design-system.md');
253
264
  fs.appendFileSync(ds, '\n<!-- leerness:na CLI 프로젝트라 디자인 토큰 없음 -->\n');
254
265
  const r = cp.spawnSync(process.execPath, [CLI, 'audit', tmp], { encoding: 'utf8' });
@@ -258,8 +269,8 @@ total++;
258
269
  }
259
270
 
260
271
  // 1.9.1 P7 회귀: init 직후 progress에 M-0001 연결 row가 자동으로 있다.
261
- total++;
262
- {
272
+ if (e2eShard.take('B(P7)')) {
273
+ total++;
263
274
  const tmp2 = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-e2e-p7-'));
264
275
  cp.spawnSync(process.execPath, [CLI, 'init', tmp2, '--yes', '--language', 'ko', '--skills', 'recommended'], { encoding: 'utf8' });
265
276
  const tracker = fs.readFileSync(path.join(tmp2, '.leerness/progress-tracker.md'), 'utf8');
@@ -280,15 +291,15 @@ run('skill use (new)', ['skill', 'use', 'open-meteo', '--note', 'first call'
280
291
  run('skill use (catalog)', ['skill', 'use', 'feature-implementation', '--note', 'catalog skill materialize', '--path', tmp]);
281
292
  run('skill optimize', ['skill', 'optimize', 'open-meteo', '--before', 'no cache', '--after', 'If-Modified-Since', '--note', 'e2e opt', '--path', tmp]);
282
293
 
283
- total++;
284
- {
294
+ if (e2eShard.take('skill.json optimizations 누적')) {
295
+ total++;
285
296
  const f = path.join(tmp, '.leerness/skills/open-meteo/skill.json');
286
297
  const ok = fs.existsSync(f) && JSON.parse(fs.readFileSync(f, 'utf8')).optimizations.length === 1;
287
298
  console.log(ok ? '✓ skill.json optimizations 누적' : '✗ skill.json optimizations 누적 실패');
288
299
  if (!ok) failed++;
289
300
  }
290
- total++;
291
- {
301
+ if (e2eShard.take('skill usage/sources/patterns 누적')) {
302
+ total++;
292
303
  const data = JSON.parse(fs.readFileSync(path.join(tmp, '.leerness/skills/open-meteo/skill.json'), 'utf8'));
293
304
  const ok = data.usage.count === 1 && data.sources.length >= 1 && data.patterns.length >= 1;
294
305
  console.log(ok ? '✓ skill usage/sources/patterns 누적' : '✗ skill usage/sources/patterns 누적 실패');
@@ -297,8 +308,8 @@ total++;
297
308
 
298
309
  run('skill consolidate', ['skill', 'consolidate', '--threshold', '0.1', '--path', tmp]);
299
310
  run('skill remove (user)', ['skill', 'remove', 'open-meteo', '--path', tmp]);
300
- total++;
301
- {
311
+ if (e2eShard.take('skill remove: 디렉토리 삭제')) {
312
+ total++;
302
313
  const ok = !fs.existsSync(path.join(tmp, '.leerness/skills/open-meteo'));
303
314
  console.log(ok ? '✓ skill remove: 디렉토리 삭제' : '✗ skill remove: 디렉토리 잔존');
304
315
  if (!ok) failed++;
@@ -324,8 +335,8 @@ run('impact: Card.html → home/about', ['impact', 'src/components/Card.html',
324
335
  run('reuse find: Card 후보', ['reuse', 'find', 'Card', '--path', tmp]);
325
336
  run('reuse register: Card', ['reuse', 'register', 'Card', '--where', 'src/components/Card.html', '--kind', 'component', '--note', 'e2e card', '--path', tmp]);
326
337
 
327
- total++;
328
- {
338
+ if (e2eShard.take('reuse-map.md에 Card row 자동 추가')) {
339
+ total++;
329
340
  const reuse = fs.readFileSync(path.join(tmp, '.leerness/reuse-map.md'), 'utf8');
330
341
  const ok = /\| Card \| src\/components\/Card\.html \| component \|/.test(reuse);
331
342
  console.log(ok ? '✓ reuse-map.md에 Card row 자동 추가' : '✗ Card row 미등록');
@@ -336,8 +347,8 @@ run('ui consistency: pass (토큰 외 색상 없음)', ['ui', 'consistency', tmp
336
347
 
337
348
  // 의도적 일탈: 토큰 외 색상 추가 → ui consistency 경고
338
349
  fs.writeFileSync(path.join(tmp, 'src/pages/contact.html'), `<style>.box { color: #abcdef; background: #123456; }</style>\n`);
339
- total++;
340
- {
350
+ if (e2eShard.take('ui consistency: 일탈 색상 #abcdef 검출 + 비0 종료')) {
351
+ total++;
341
352
  const r = cp.spawnSync(process.execPath, [CLI, 'ui', 'consistency', tmp, '--fail-on-violation'], { encoding: 'utf8' });
342
353
  const ok = r.status === 1 && /토큰 외 값/.test(r.stdout) && /#abcdef/i.test(r.stdout);
343
354
  console.log(ok ? '✓ ui consistency: 일탈 색상 #abcdef 검출 + 비0 종료' : '✗ ui consistency 일탈 미검출');
@@ -348,8 +359,8 @@ run('graph: mermaid 출력', ['graph', tmp]);
348
359
  run('guide: 통합 가이드', ['guide', 'src/components/Card.html', '--path', tmp]);
349
360
 
350
361
  // 1.9.4 회귀: impact strong/weak 구분
351
- total++;
352
- {
362
+ if (e2eShard.take('B(A)')) {
363
+ total++;
353
364
  // home.html에 "Card"라는 식별자가 plain text로 들어가도록 (false positive 시드)
354
365
  fs.appendFileSync(path.join(tmp, 'src/pages/home.html'), '\n<!-- 카드 콘텐츠는 Cards 배열로 -->\n');
355
366
  const r = cp.spawnSync(process.execPath, [CLI, 'impact', 'src/components/Card.html', '--path', tmp], { encoding: 'utf8' });
@@ -360,8 +371,8 @@ total++;
360
371
  }
361
372
 
362
373
  // 1.9.16 회귀: brainstorm --all-apps / --json / session close 워크스페이스 안내
363
- total++;
364
- {
374
+ if (e2eShard.take('B(1.9.16)')) {
375
+ total++;
365
376
  // brainstorm --all-apps
366
377
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bsa-'));
367
378
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bsb-'));
@@ -375,8 +386,8 @@ total++;
375
386
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
376
387
  }
377
388
 
378
- total++;
379
- {
389
+ if (e2eShard.take('B(1.9.16) #2')) {
390
+ total++;
380
391
  // --json 단일 brainstorm
381
392
  const tmpJ = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-json-'));
382
393
  cp.spawnSync(process.execPath, [CLI, 'init', tmpJ, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -389,8 +400,8 @@ total++;
389
400
  if (!ok) failed++;
390
401
  }
391
402
 
392
- total++;
393
- {
403
+ if (e2eShard.take('B(1.9.16) #3')) {
404
+ total++;
394
405
  // retro --json
395
406
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rj-'));
396
407
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -402,8 +413,8 @@ total++;
402
413
  if (!ok) failed++;
403
414
  }
404
415
 
405
- total++;
406
- {
416
+ if (e2eShard.take('B(1.9.16) #4')) {
417
+ total++;
407
418
  // insights --json (workspace)
408
419
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iwsa-'));
409
420
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iwsb-'));
@@ -417,8 +428,8 @@ total++;
417
428
  if (!ok) failed++;
418
429
  }
419
430
 
420
- total++;
421
- {
431
+ if (e2eShard.take('B(1.9.16) #5')) {
432
+ total++;
422
433
  // session close 끝에 워크스페이스 안내 (다른 leerness 프로젝트 시뮬)
423
434
  const wsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ws-'));
424
435
  fs.mkdirSync(path.join(wsRoot, '_apps'), { recursive: true });
@@ -435,8 +446,8 @@ total++;
435
446
  }
436
447
 
437
448
  // 1.9.17 회귀: handoff --all-apps / reuse-map --all-apps (워크스페이스 오케스트레이션)
438
- total++;
439
- {
449
+ if (e2eShard.take('B(1.9.17)')) {
450
+ total++;
440
451
  // handoff --include
441
452
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ha-'));
442
453
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-hb-'));
@@ -448,8 +459,8 @@ total++;
448
459
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
449
460
  }
450
461
 
451
- total++;
452
- {
462
+ if (e2eShard.take('B(1.9.17) #2')) {
463
+ total++;
453
464
  // handoff --include --json
454
465
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-haj-'));
455
466
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-hbj-'));
@@ -463,8 +474,8 @@ total++;
463
474
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
464
475
  }
465
476
 
466
- total++;
467
- {
477
+ if (e2eShard.take('B(1.9.17) #3')) {
478
+ total++;
468
479
  // reuse-map 단일 + 워크스페이스 모드 + 중복 감지
469
480
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rma-'));
470
481
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rmb-'));
@@ -492,8 +503,8 @@ total++;
492
503
  }
493
504
 
494
505
  // 1.9.18 회귀: handoff --since / reuse-map --strict-elements + depends-on / verify-claim
495
- total++;
496
- {
506
+ if (e2eShard.take('B(1.9.18)')) {
507
+ total++;
497
508
  // handoff --since: 최근 변경 강조
498
509
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sincea-'));
499
510
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sinceb-'));
@@ -508,8 +519,8 @@ total++;
508
519
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
509
520
  }
510
521
 
511
- total++;
512
- {
522
+ if (e2eShard.take('B(1.9.18) #2')) {
523
+ total++;
513
524
  // handoff --since 형식 오류 → fail
514
525
  const tmpE = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sincee-'));
515
526
  cp.spawnSync(process.execPath, [CLI, 'init', tmpE, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -519,8 +530,8 @@ total++;
519
530
  if (!ok) failed++;
520
531
  }
521
532
 
522
- total++;
523
- {
533
+ if (e2eShard.take('B(1.9.18) #3')) {
534
+ total++;
524
535
  // reuse-map --strict-elements: 같은 함수명 다른 capability 감지
525
536
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-strict-a-'));
526
537
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-strict-b-'));
@@ -545,8 +556,8 @@ total++;
545
556
  if (!ok) { failed++; console.log(r2.stdout.slice(0, 500)); }
546
557
  }
547
558
 
548
- total++;
549
- {
559
+ if (e2eShard.take('B(1.9.18) #4')) {
560
+ total++;
550
561
  // reuse-map depends-on: notes 컬럼에서 의존 추출
551
562
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-deps-'));
552
563
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -559,8 +570,8 @@ total++;
559
570
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
560
571
  }
561
572
 
562
- total++;
563
- {
573
+ if (e2eShard.take('pass (실측 ≥ 주장)')) {
574
+ total++;
564
575
  // verify-claim: 파일 존재 + 테스트 카운트 검증
565
576
  const tmpV = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vc-'));
566
577
  cp.spawnSync(process.execPath, [CLI, 'init', tmpV, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -592,8 +603,8 @@ total++;
592
603
  }
593
604
 
594
605
  // 1.9.19 회귀: verify-claim --run-tests + --strict-elements same-file 구분
595
- total++;
596
- {
606
+ if (e2eShard.take('B(1.9.19)')) {
607
+ total++;
597
608
  // verify-claim --run-tests: npm test 자동 실행 + 주장 vs 실행 결과 대조
598
609
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rt-'));
599
610
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -619,8 +630,8 @@ total++;
619
630
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
620
631
  }
621
632
 
622
- total++;
623
- {
633
+ if (e2eShard.take('B(1.9.19) #2')) {
634
+ total++;
624
635
  // --run-tests 실패 케이스: 5/5 주장 vs 실제 3/5 실행 → 불일치 + exit 1
625
636
  const tmpF = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rtf-'));
626
637
  cp.spawnSync(process.execPath, [CLI, 'init', tmpF, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -640,8 +651,8 @@ total++;
640
651
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
641
652
  }
642
653
 
643
- total++;
644
- {
654
+ if (e2eShard.take('B(1.9.19) #3')) {
655
+ total++;
645
656
  // --strict-elements: same-file ⚠ vs diff-file ℹ 구분
646
657
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-strict2-a-'));
647
658
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-strict2-b-'));
@@ -668,8 +679,8 @@ total++;
668
679
  }
669
680
 
670
681
  // 1.9.20 회귀: verify-claim file regex 확장 + jest/mocha 파싱 + --bench
671
- total++;
672
- {
682
+ if (e2eShard.take('B(1.9.20)')) {
683
+ total++;
673
684
  // verify-claim regex: 도메인 폴더 (scenes/scripts) + 루트 메타 파일 (project.godot)
674
685
  const tmpG = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-godot-'));
675
686
  cp.spawnSync(process.execPath, [CLI, 'init', tmpG, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -691,8 +702,8 @@ total++;
691
702
  }
692
703
 
693
704
  // 1.9.21 회귀: 설정 파일 확장자 (.cfg/.ini/.env/.toml/.lock) 추가
694
- total++;
695
- {
705
+ if (e2eShard.take('B(1.9.21)')) {
706
+ total++;
696
707
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cfg-'));
697
708
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
698
709
  fs.writeFileSync(path.join(tmpC, 'export_presets.cfg'), '[preset.0]\n');
@@ -710,8 +721,8 @@ total++;
710
721
  }
711
722
 
712
723
  // 1.9.25 회귀: memory search --include-code + brainstorm --include-code + register-pending
713
- total++;
714
- {
724
+ if (e2eShard.take('B(1.9.25)')) {
725
+ total++;
715
726
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-incode-'));
716
727
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
717
728
  fs.mkdirSync(path.join(tmpC, 'src'), { recursive: true });
@@ -725,8 +736,8 @@ total++;
725
736
  if (!ok) { failed++; console.log(r2.stdout.slice(0, 400)); }
726
737
  }
727
738
 
728
- total++;
729
- {
739
+ if (e2eShard.take('B(1.9.25) #2')) {
740
+ total++;
730
741
  // register-pending
731
742
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-regp-'));
732
743
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -741,8 +752,8 @@ total++;
741
752
  }
742
753
 
743
754
  // 1.9.26 회귀: optimism-check (낙관적 표시 자동 감지) + verify-claim --strict-claims
744
- total++;
745
- {
755
+ if (e2eShard.take('B(1.9.26)')) {
756
+ total++;
746
757
  const tmpO = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-optm-'));
747
758
  cp.spawnSync(process.execPath, [CLI, 'init', tmpO, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
748
759
  fs.mkdirSync(path.join(tmpO, 'src'), { recursive: true });
@@ -765,8 +776,8 @@ total++;
765
776
  if (!ok) { failed++; console.log('--- 거짓 case ---\n' + r1.stdout.slice(0, 300) + '\n--- 정상 case ---\n' + r2.stdout.slice(0, 300)); }
766
777
  }
767
778
 
768
- total++;
769
- {
779
+ if (e2eShard.take('B(1.9.26) #2')) {
780
+ total++;
770
781
  // verify-claim --strict-claims 통합
771
782
  const tmpS = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-stct-'));
772
783
  cp.spawnSync(process.execPath, [CLI, 'init', tmpS, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -781,8 +792,8 @@ total++;
781
792
  }
782
793
 
783
794
  // 1.9.27 회귀: URL/메서드 매핑 + 신뢰도 점수 + 신규 카테고리 (FileIO/Queue/Cache/Notify/Storage)
784
- total++;
785
- {
795
+ if (e2eShard.take('B(1.9.27)')) {
796
+ total++;
786
797
  // T-9001 false negative 해결 검증: 코드에 다른 목적의 http.request 있어도 URL 미매치로 잡아냄
787
798
  const tmpU = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-url-'));
788
799
  cp.spawnSync(process.execPath, [CLI, 'init', tmpU, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -801,8 +812,8 @@ total++;
801
812
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
802
813
  }
803
814
 
804
- total++;
805
- {
815
+ if (e2eShard.take('B(1.9.27) #2')) {
816
+ total++;
806
817
  // 신규 카테고리 (Slack/Notify)
807
818
  const tmpN = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-notify-'));
808
819
  cp.spawnSync(process.execPath, [CLI, 'init', tmpN, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -816,8 +827,8 @@ total++;
816
827
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
817
828
  }
818
829
 
819
- total++;
820
- {
830
+ if (e2eShard.take('B(1.9.27) #3')) {
831
+ total++;
821
832
  // 신뢰도 점수 — 정상 케이스는 1.0
822
833
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-conf-'));
823
834
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -834,8 +845,8 @@ total++;
834
845
  }
835
846
 
836
847
  // 1.9.28 회귀: 한국형 PG (카카오페이) 패턴 + confidence floor 0.15
837
- total++;
838
- {
848
+ if (e2eShard.take('B(1.9.28)')) {
849
+ total++;
839
850
  const tmpK = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-kpay-'));
840
851
  cp.spawnSync(process.execPath, [CLI, 'init', tmpK, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
841
852
  fs.mkdirSync(path.join(tmpK, 'src'), { recursive: true });
@@ -853,8 +864,8 @@ total++;
853
864
  }
854
865
 
855
866
  // 1.9.29 회귀: 페르소나 시스템 + review 명령
856
- total++;
857
- {
867
+ if (e2eShard.take('B(1.9.29)')) {
868
+ total++;
858
869
  // persona list — 5 내장
859
870
  const r1 = cp.spawnSync(process.execPath, [CLI, 'persona', 'list'], { encoding: 'utf8', timeout: 10000 });
860
871
  const okList = r1.status === 0
@@ -874,8 +885,8 @@ total++;
874
885
  if (!ok) { failed++; console.log(r1.stdout.slice(0, 400)); }
875
886
  }
876
887
 
877
- total++;
878
- {
888
+ if (e2eShard.take('B(1.9.29) #2')) {
889
+ total++;
879
890
  // review <file> --persona X
880
891
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rev-'));
881
892
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -901,8 +912,8 @@ total++;
901
912
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
902
913
  }
903
914
 
904
- total++;
905
- {
915
+ if (e2eShard.take('B(1.9.29) #3')) {
916
+ total++;
906
917
  // 사용자 정의 페르소나 add
907
918
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-padd-'));
908
919
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -919,8 +930,8 @@ total++;
919
930
  }
920
931
 
921
932
  // 1.9.30 회귀: 외부 CLI 오케스트레이션 (agents list/check/dispatch)
922
- total++;
923
- {
933
+ if (e2eShard.take('B(1.9.30+1.9.277)')) {
934
+ total++;
924
935
  // agents list — claude가 환경변수 + PATH 둘 다 충족 시 ready
925
936
  const env1 = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_GROK: '0', LEERNESS_ENABLE_COPILOT: '0' };
926
937
  const r1 = cp.spawnSync(process.execPath, [CLI, 'agents', 'list'], { encoding: 'utf8', timeout: 90000, env: env1 });
@@ -945,8 +956,8 @@ total++;
945
956
  if (!ok) { failed++; console.log(r1.stdout.slice(0, 500)); }
946
957
  }
947
958
 
948
- total++;
949
- {
959
+ if (e2eShard.take('B(1.9.30)')) {
960
+ total++;
950
961
  // agents dispatch — 활성 미충족 시 거부
951
962
  const env = { ...process.env, LEERNESS_ENABLE_CODEX: '0' };
952
963
  // 1.30.2: timeout 10s→30s flake 하드닝(1.9.375 계열) — 전체 e2e 부하(수백 spawn) 하에서 짧은 타임아웃이 간헐 빈-stdout→오판.
@@ -964,8 +975,8 @@ total++;
964
975
  }
965
976
 
966
977
  // 1.36.185 회귀: agents quota는 설치·활성·인증·로컬 라우팅·모델 호출·잔여량을 분리하고 추정하지 않는다
967
- total++;
968
- {
978
+ if (e2eShard.take('B(1.36.185)')) {
979
+ total++;
969
980
  // agents quota — env=0 시 인증 확인을 실행하지 않고 disabled/not-installed를 정직하게 표시
970
981
  const env = { ...process.env };
971
982
  for (const key of Object.keys(env)) if (/^LEERNESS_ENABLE_/i.test(key)) delete env[key];
@@ -1020,8 +1031,8 @@ total++;
1020
1031
  console.log(ok ? '✓ B(1.36.185) agents quota: 관측 축 분리·비밀 비출력·비활성 인증 무실행·추정 금지' : `✗ quota 실패 (text=${okText} json=${okJson})`);
1021
1032
  if (!ok) { failed++; console.log((r.stdout + '\n' + r2.stdout + '\n' + r2.stderr).slice(0, 1400)); }
1022
1033
  }
1023
- total++;
1024
- {
1034
+ if (e2eShard.take('B(1.9.31)')) {
1035
+ total++;
1025
1036
  // 사용법 메시지에 quota 포함
1026
1037
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'foo'], { encoding: 'utf8', timeout: 10000 });
1027
1038
  const usage = r.stdout + r.stderr;
@@ -1033,8 +1044,8 @@ total++;
1033
1044
  }
1034
1045
 
1035
1046
  // Historical banner checks and the removed CLI-agent activation surface.
1036
- total++;
1037
- {
1047
+ if (e2eShard.take('B(1.9.32)')) {
1048
+ total++;
1038
1049
  // --version --banner: LEERNESS ASCII + 신규 슬로건 (1.9.144+ "AI 에이전트 검수·기억·드리프트 방지 하네스")
1039
1050
  const r = cp.spawnSync(process.execPath, [CLI, '--version', '--banner'], { encoding: 'utf8', timeout: 30000, env: { ...process.env, TERM: 'dumb' } });
1040
1051
  const ok = r.status === 0
@@ -1046,8 +1057,8 @@ total++;
1046
1057
  if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
1047
1058
  }
1048
1059
 
1049
- total++;
1050
- {
1060
+ if (e2eShard.take('B(1.9.32) #2')) {
1061
+ total++;
1051
1062
  // LEERNESS_NO_BANNER=1: 배너 스킵
1052
1063
  const r = cp.spawnSync(process.execPath, [CLI, '--version', '--banner'], { encoding: 'utf8', timeout: 10000, env: { ...process.env, LEERNESS_NO_BANNER: '1' } });
1053
1064
  const ok = r.status === 0 && !/███████╗/.test(r.stdout) && /^1\./m.test(r.stdout.trim());
@@ -1055,8 +1066,8 @@ total++;
1055
1066
  if (!ok) { failed++; console.log(r.stdout.slice(0, 300)); }
1056
1067
  }
1057
1068
 
1058
- total++;
1059
- {
1069
+ if (e2eShard.take('init: 에이전트 활성화 문항/기본값 없이 지침 설치')) {
1070
+ total++;
1060
1071
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-setup-removed-'));
1061
1072
  const r = cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended', '--minimal', '--no-banner'], { encoding: 'utf8', timeout: 30000 });
1062
1073
  const configPath = path.join(tmpC, '.leerness', 'leerness-config.json');
@@ -1068,8 +1079,8 @@ total++;
1068
1079
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1069
1080
  }
1070
1081
 
1071
- total++;
1072
- {
1082
+ if (e2eShard.take('--help: 제거된 설정 명령 없음')) {
1083
+ total++;
1073
1084
  // Removed command is not advertised.
1074
1085
  const r = cp.spawnSync(process.execPath, [CLI, '--help'], { encoding: 'utf8', timeout: 10000 });
1075
1086
  const ok = r.status === 0 && !/setup-agents|CLI 에이전트 활성화/.test(r.stdout);
@@ -1077,8 +1088,8 @@ total++;
1077
1088
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1078
1089
  }
1079
1090
 
1080
- total++;
1081
- {
1091
+ if (e2eShard.take('제거된 setup-agents 명령은 설정을 변경하지 않음')) {
1092
+ total++;
1082
1093
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-setup-disabled-'));
1083
1094
  const tmpEnv = path.join(tmpC, '.env');
1084
1095
  fs.writeFileSync(tmpEnv, 'EXISTING=1\nLEERNESS_ENABLE_CLAUDE=1\n', 'utf8');
@@ -1090,8 +1101,8 @@ total++;
1090
1101
  }
1091
1102
 
1092
1103
  // 1.9.33 회귀: npx 캐시 함정 — stale 버전 실행 시 경고
1093
- total++;
1094
- {
1104
+ if (e2eShard.take('B(1.9.33)')) {
1105
+ total++;
1095
1106
  // 캐시에 미래 버전을 심어 stale 시뮬레이션 → 경고 출력 + init은 계속 진행
1096
1107
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-stale-'));
1097
1108
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended', '--no-stale-check'], { stdio: 'ignore', timeout: 30000 });
@@ -1109,8 +1120,8 @@ total++;
1109
1120
  }
1110
1121
 
1111
1122
  // 1.9.63/64 회귀
1112
- total++;
1113
- {
1123
+ if (e2eShard.take('B(1.9.63)')) {
1124
+ total++;
1114
1125
  // audit --strict
1115
1126
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-st-'));
1116
1127
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1120,8 +1131,8 @@ total++;
1120
1131
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1121
1132
  }
1122
1133
 
1123
- total++;
1124
- {
1134
+ if (e2eShard.take('B(1.9.63) #2')) {
1135
+ total++;
1125
1136
  // audit --strict --threshold 10 → exit 0
1126
1137
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-st2-'));
1127
1138
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1131,8 +1142,8 @@ total++;
1131
1142
  if (!ok) { failed++; console.log(r.stdout.slice(-300)); }
1132
1143
  }
1133
1144
 
1134
- total++;
1135
- {
1145
+ if (e2eShard.take('B(1.9.64)')) {
1146
+ total++;
1136
1147
  // install 별칭
1137
1148
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iv-'));
1138
1149
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1146,8 +1157,8 @@ total++;
1146
1157
  }
1147
1158
 
1148
1159
  // 1.9.60/61/62 회귀
1149
- total++;
1150
- {
1160
+ if (e2eShard.take('B(1.9.60)')) {
1161
+ total++;
1151
1162
  // task export → TodoWrite JSON 호환
1152
1163
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tex-'));
1153
1164
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1160,8 +1171,8 @@ total++;
1160
1171
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1161
1172
  }
1162
1173
 
1163
- total++;
1164
- {
1174
+ if (e2eShard.take('B(1.9.61)')) {
1175
+ total++;
1165
1176
  // MCP cursor 페이지네이션 (chunkSize override)
1166
1177
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mcc-'));
1167
1178
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1173,8 +1184,8 @@ total++;
1173
1184
  if (!ok) { failed++; console.log(JSON.stringify(resp).slice(0, 300)); }
1174
1185
  }
1175
1186
 
1176
- total++;
1177
- {
1187
+ if (e2eShard.take('B(1.9.62)')) {
1188
+ total++;
1178
1189
  // audit npm CVE — OFFLINE 또는 package.json 없을 때 graceful
1179
1190
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ac-'));
1180
1191
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1186,8 +1197,8 @@ total++;
1186
1197
  }
1187
1198
 
1188
1199
  // 1.9.58/59 회귀
1189
- total++;
1190
- {
1200
+ if (e2eShard.take('B(1.9.58)')) {
1201
+ total++;
1191
1202
  // fuzzy 매칭 — 어간 변형 (webhook ↔ webhooks)
1192
1203
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-fz-'));
1193
1204
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1200,8 +1211,8 @@ total++;
1200
1211
  if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
1201
1212
  }
1202
1213
 
1203
- total++;
1204
- {
1214
+ if (e2eShard.take('B(1.9.59)')) {
1215
+ total++;
1205
1216
  // session close default suggest
1206
1217
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sd-'));
1207
1218
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1212,8 +1223,8 @@ total++;
1212
1223
  if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
1213
1224
  }
1214
1225
 
1215
- total++;
1216
- {
1226
+ if (e2eShard.take('B(1.9.59) #2')) {
1227
+ total++;
1217
1228
  // --no-suggest 비활성 (이전 동작 보존)
1218
1229
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ns-'));
1219
1230
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1224,8 +1235,8 @@ total++;
1224
1235
  }
1225
1236
 
1226
1237
  // 1.9.56/57 회귀
1227
- total++;
1228
- {
1238
+ if (e2eShard.take('B(1.9.56)')) {
1239
+ total++;
1229
1240
  // handoff 자동 lessons 재상기
1230
1241
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ha-'));
1231
1242
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1238,8 +1249,8 @@ total++;
1238
1249
  if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
1239
1250
  }
1240
1251
 
1241
- total++;
1242
- {
1252
+ if (e2eShard.take('B(1.9.57)')) {
1253
+ total++;
1243
1254
  // session close --suggest
1244
1255
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sc-'));
1245
1256
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1250,8 +1261,8 @@ total++;
1250
1261
  }
1251
1262
 
1252
1263
  // 1.9.54/55 회귀
1253
- total++;
1254
- {
1264
+ if (e2eShard.take('B(1.9.54)')) {
1265
+ total++;
1255
1266
  // lessons --auto 키워드 자동 추출
1256
1267
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-la-'));
1257
1268
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1264,8 +1275,8 @@ total++;
1264
1275
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1265
1276
  }
1266
1277
 
1267
- total++;
1268
- {
1278
+ if (e2eShard.take('B(1.9.55)')) {
1279
+ total++;
1269
1280
  // MCP server tools/list 12 도구 (1.9.55)
1270
1281
  const r = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], {
1271
1282
  encoding: 'utf8', timeout: 8000,
@@ -1281,8 +1292,8 @@ total++;
1281
1292
  }
1282
1293
 
1283
1294
  // 1.9.53 회귀: skill suggest 자동 학습
1284
- total++;
1285
- {
1295
+ if (e2eShard.take('B(1.9.53)')) {
1296
+ total++;
1286
1297
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sg-'));
1287
1298
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
1288
1299
  // 반복 키워드 6회
@@ -1298,8 +1309,8 @@ total++;
1298
1309
  }
1299
1310
 
1300
1311
  // 1.9.51/52 회귀
1301
- total++;
1302
- {
1312
+ if (e2eShard.take('B(1.9.51)')) {
1313
+ total++;
1303
1314
  // benchmark --scenario all → 4개 시나리오 모두 감지
1304
1315
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sc-'));
1305
1316
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1311,8 +1322,8 @@ total++;
1311
1322
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1312
1323
  }
1313
1324
 
1314
- total++;
1315
- {
1325
+ if (e2eShard.take('B(1.9.51) #2')) {
1326
+ total++;
1316
1327
  // benchmark --scenario 알 수 없는 ID → 친절 안내
1317
1328
  const r = cp.spawnSync(process.execPath, [CLI, 'benchmark', '--scenario', 'unknown-x'], { encoding: 'utf8', timeout: 15000 });
1318
1329
  const ok = r.status !== 0 && /알 수 없는 scenario/.test(r.stdout + r.stderr);
@@ -1320,8 +1331,8 @@ total++;
1320
1331
  if (!ok) { failed++; console.log((r.stdout + r.stderr).slice(0, 300)); }
1321
1332
  }
1322
1333
 
1323
- total++;
1324
- {
1334
+ if (e2eShard.take('B(1.9.52)')) {
1335
+ total++;
1325
1336
  // _parseSkillCatalog 4 형식 인식 — 1.9.370 (UR-0025): lib/pure-utils 직접 require (이전: harness 소스 regex+eval, 모듈 이동으로 전환)
1326
1337
  const fn = require(path.resolve(__dirname, '..', 'lib', 'pure-utils'))._parseSkillCatalog;
1327
1338
  if (typeof fn !== 'function') {
@@ -1340,8 +1351,8 @@ total++;
1340
1351
  }
1341
1352
 
1342
1353
  // 1.9.48~50 회귀
1343
- total++;
1344
- {
1354
+ if (e2eShard.take('B(1.9.48)')) {
1355
+ total++;
1345
1356
  // 1.9.48 cross-platform archive — PowerShell ZIP or tar
1346
1357
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-arc-'));
1347
1358
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
@@ -1354,8 +1365,8 @@ total++;
1354
1365
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1355
1366
  }
1356
1367
 
1357
- total++;
1358
- {
1368
+ if (e2eShard.take('B(1.9.49)')) {
1369
+ total++;
1359
1370
  // 1.9.49 benchmark --measure 인자 검증
1360
1371
  const r = cp.spawnSync(process.execPath, [CLI, 'benchmark', '--measure'], { encoding: 'utf8', timeout: 15000 });
1361
1372
  const ok = r.status !== 0 && /사용법|task/.test(r.stdout + r.stderr);
@@ -1363,8 +1374,8 @@ total++;
1363
1374
  if (!ok) { failed++; console.log((r.stdout + r.stderr).slice(0, 300)); }
1364
1375
  }
1365
1376
 
1366
- total++;
1367
- {
1377
+ if (e2eShard.take('B(1.9.50)')) {
1378
+ total++;
1368
1379
  // 1.9.50 skill match --embedding (Ollama URL 없을 때 거부)
1369
1380
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-emb-'));
1370
1381
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1377,8 +1388,8 @@ total++;
1377
1388
  }
1378
1389
 
1379
1390
  // 1.9.45 회귀: skill match — 키워드 매칭 추천 (jaccard)
1380
- total++;
1381
- {
1391
+ if (e2eShard.take('B(1.9.45)')) {
1392
+ total++;
1382
1393
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-match-'));
1383
1394
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
1384
1395
  const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'match', '기능 구현 재사용 검사', '--path', tmpC, '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -1392,8 +1403,8 @@ total++;
1392
1403
  }
1393
1404
 
1394
1405
  // 1.9.46 회귀: benchmark — 자체 6차원 점수 + 타도구 비교
1395
- total++;
1396
- {
1406
+ if (e2eShard.take('B(1.9.46)')) {
1407
+ total++;
1397
1408
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bench-'));
1398
1409
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
1399
1410
  const r = cp.spawnSync(process.execPath, [CLI, 'benchmark', tmpC, '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -1410,8 +1421,8 @@ total++;
1410
1421
  }
1411
1422
 
1412
1423
  // 1.9.47 회귀: skill publish — 9개 SKILL.md export + manifest.json
1413
- total++;
1414
- {
1424
+ if (e2eShard.take('B(1.9.47)')) {
1425
+ total++;
1415
1426
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-pub-'));
1416
1427
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
1417
1428
  const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'publish', '--path', tmpC, '--bundle-only'], { encoding: 'utf8', timeout: 30000 });
@@ -1429,8 +1440,8 @@ total++;
1429
1440
  }
1430
1441
 
1431
1442
  // 1.9.44 회귀: BOM SKILL.md 처리 (stress-v2 G2 발견)
1432
- total++;
1433
- {
1443
+ if (e2eShard.take('B(1.9.44)')) {
1444
+ total++;
1434
1445
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bom-'));
1435
1446
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
1436
1447
  const src = path.join(tmpC, 'bom.md');
@@ -1445,8 +1456,8 @@ total++;
1445
1456
  }
1446
1457
 
1447
1458
  // 1.9.43 회귀: MCP server + skill export-all + _reports 비공개
1448
- total++;
1449
- {
1459
+ if (e2eShard.take('B(1.9.43)')) {
1460
+ total++;
1450
1461
  // skill export-all: 모든 내장 skill을 SKILL.md로 일괄 export
1451
1462
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-exall-'));
1452
1463
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'all'], { stdio: 'ignore', timeout: 30000 });
@@ -1459,8 +1470,8 @@ total++;
1459
1470
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1460
1471
  }
1461
1472
 
1462
- total++;
1463
- {
1473
+ if (e2eShard.take('B(1.9.43) #2')) {
1474
+ total++;
1464
1475
  // MCP server initialize: stdio JSON-RPC 정상 응답
1465
1476
  const r = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], {
1466
1477
  encoding: 'utf8', timeout: 10000,
@@ -1478,8 +1489,8 @@ total++;
1478
1489
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1479
1490
  }
1480
1491
 
1481
- total++;
1482
- {
1492
+ if (e2eShard.take('B(1.9.43) #3')) {
1493
+ total++;
1483
1494
  // MCP server tools/list: 10개 도구 노출
1484
1495
  const r = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], {
1485
1496
  encoding: 'utf8', timeout: 10000,
@@ -1498,7 +1509,7 @@ total++;
1498
1509
 
1499
1510
  // 1.36.55 (외부감사 이식성): .gitignore/.npmignore 는 npm files 화이트리스트에서 의도적으로 제외되는 저장소-전용 파일 —
1500
1511
  // 게시 아티팩트에서는 검사 자체가 성립 안 하므로 명시 SKIP (조용한 누락이 아니라 사유 로그).
1501
- {
1512
+ if (e2eShard.take('B(1.9.43) #4')) {
1502
1513
  const giPath = path.join(__dirname, '..', '.gitignore');
1503
1514
  if (!fs.existsSync(giPath)) console.log('⊘ SKIP(아티팩트 프로필): leerness-pkg/.gitignore 검사 — 저장소 체크아웃 전용');
1504
1515
  else {
@@ -1510,7 +1521,7 @@ total++;
1510
1521
  }
1511
1522
  }
1512
1523
 
1513
- {
1524
+ if (e2eShard.take('B(1.9.43) #5')) {
1514
1525
  const niPath = path.join(__dirname, '..', '.npmignore');
1515
1526
  if (!fs.existsSync(niPath)) console.log('⊘ SKIP(아티팩트 프로필): leerness-pkg/.npmignore 검사 — 저장소 체크아웃 전용');
1516
1527
  else {
@@ -1523,8 +1534,8 @@ total++;
1523
1534
  }
1524
1535
 
1525
1536
  // 1.9.42 회귀: agentskills.io 표준 호환 (skill install/discover/export + .env opt-in)
1526
- total++;
1527
- {
1537
+ if (e2eShard.take('B(1.9.42)')) {
1538
+ total++;
1528
1539
  // skill discover: env 없으면 opt-in 안내로 거부
1529
1540
  const env = { ...process.env };
1530
1541
  delete env.LEERNESS_SKILL_DISCOVER_URL;
@@ -1534,8 +1545,8 @@ total++;
1534
1545
  if (!ok) { failed++; console.log((r.stdout + r.stderr).slice(0, 400)); }
1535
1546
  }
1536
1547
 
1537
- total++;
1538
- {
1548
+ if (e2eShard.take('B(1.9.42) #2')) {
1549
+ total++;
1539
1550
  // skill export → SKILL.md frontmatter 정확 생성
1540
1551
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-skex-'));
1541
1552
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1551,8 +1562,8 @@ total++;
1551
1562
  if (!ok) { failed++; console.log(body.slice(0, 300) || r.stdout.slice(0, 300)); }
1552
1563
  }
1553
1564
 
1554
- total++;
1555
- {
1565
+ if (e2eShard.take('B(1.9.42) #3')) {
1566
+ total++;
1556
1567
  // skill install: 로컬 SKILL.md import → .leerness/skills/<id>/SKILL.md 자동 배치
1557
1568
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-skin-'));
1558
1569
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1568,8 +1579,8 @@ total++;
1568
1579
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1569
1580
  }
1570
1581
 
1571
- total++;
1572
- {
1582
+ if (e2eShard.take('B(1.9.42) #4')) {
1583
+ total++;
1573
1584
  // skill install이 skill.json도 자동 작성 (자체 catalog 호환)
1574
1585
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-skin2-'));
1575
1586
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1587,8 +1598,8 @@ total++;
1587
1598
  }
1588
1599
 
1589
1600
  // 1.9.41 회귀: whats-new 명령 + migrate 차분 AI must re-read + handoff fresh 알림
1590
- total++;
1591
- {
1601
+ if (e2eShard.take('B(1.9.41)')) {
1602
+ total++;
1592
1603
  // whats-new --from 큰 점프 → 신규 명령 추출
1593
1604
  // 1.36.69: 기본 응답은 최신 30개로 제한됨 — 전체 차분 검증은 --all (대용량이라 maxBuffer 명시)
1594
1605
  const r = cp.spawnSync(process.execPath, [CLI, 'whats-new', '--from', '1.9.33', '--json', '--all'], { encoding: 'utf8', timeout: 20000, maxBuffer: 32 * 1024 * 1024 });
@@ -1603,8 +1614,8 @@ total++;
1603
1614
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1604
1615
  }
1605
1616
 
1606
- total++;
1607
- {
1617
+ if (e2eShard.take('B(1.9.41) #2')) {
1618
+ total++;
1608
1619
  // migrate가 fromV가 있는 경우 AI must re-read 블록을 stdout에 출력
1609
1620
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mig-'));
1610
1621
  // 1.9.30 표시로 init한 척 (HARNESS_VERSION 직접 작성)
@@ -1619,8 +1630,8 @@ total++;
1619
1630
  if (!ok) { failed++; console.log(r.stdout.slice(-800)); }
1620
1631
  }
1621
1632
 
1622
- total++;
1623
- {
1633
+ if (e2eShard.take('B(1.9.41) #3')) {
1634
+ total++;
1624
1635
  // migration-report.md에 AI must re-read 섹션 영구 기록
1625
1636
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mig2-'));
1626
1637
  fs.mkdirSync(path.join(tmpC, '.leerness'), { recursive: true });
@@ -1633,8 +1644,8 @@ total++;
1633
1644
  if (!ok) { failed++; console.log(body.slice(0, 600)); }
1634
1645
  }
1635
1646
 
1636
- total++;
1637
- {
1647
+ if (e2eShard.take('B(1.9.41) #4')) {
1648
+ total++;
1638
1649
  // handoff가 fresh migration-report (24h 내) 시 자동 알림
1639
1650
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-fresh-'));
1640
1651
  fs.mkdirSync(path.join(tmpC, '.leerness'), { recursive: true });
@@ -1652,8 +1663,8 @@ total++;
1652
1663
  }
1653
1664
 
1654
1665
  // 1.9.40 회귀: release pack 통합 명령 + audit README mismatch 감지
1655
- total++;
1656
- {
1666
+ if (e2eShard.take('B(1.9.40/1.36.144)')) {
1667
+ total++;
1657
1668
  // release pack --dry-run --task-add: 자동 task 등록
1658
1669
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rp-'));
1659
1670
  // init 후 가벼운 package.json 흉내 (release pack은 npm pack 시도하므로 dry-run으로 우회)
@@ -1681,8 +1692,8 @@ total++;
1681
1692
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); console.log('--- real ---'); console.log(r2.stdout.slice(0, 400)); }
1682
1693
  }
1683
1694
 
1684
- total++;
1685
- {
1695
+ if (e2eShard.take('B(1.9.40)')) {
1696
+ total++;
1686
1697
  // release pack --parent-migrate (인공 parent .leerness 생성)
1687
1698
  const tmpParent = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rp-parent-'));
1688
1699
  cp.spawnSync(process.execPath, [CLI, 'init', tmpParent, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1696,8 +1707,8 @@ total++;
1696
1707
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1697
1708
  }
1698
1709
 
1699
- total++;
1700
- {
1710
+ if (e2eShard.take('B(1.9.40) #2')) {
1711
+ total++;
1701
1712
  // audit README version mismatch 감지
1702
1713
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mm-'));
1703
1714
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1710,8 +1721,8 @@ total++;
1710
1721
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1711
1722
  }
1712
1723
 
1713
- total++;
1714
- {
1724
+ if (e2eShard.take('B(1.9.40) #3')) {
1725
+ total++;
1715
1726
  // audit --fix가 README 자동 갱신
1716
1727
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mm2-'));
1717
1728
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1725,8 +1736,8 @@ total++;
1725
1736
  }
1726
1737
 
1727
1738
  // 1.9.39 회귀: session workflow 가이드 + auto-fix + auto-recover
1728
- total++;
1729
- {
1739
+ if (e2eShard.take('B(1.9.39)')) {
1740
+ total++;
1730
1741
  // handoff 끝에 워크플로 6단계 가이드 자동 표시
1731
1742
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wf-'));
1732
1743
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1739,8 +1750,8 @@ total++;
1739
1750
  if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
1740
1751
  }
1741
1752
 
1742
- total++;
1743
- {
1753
+ if (e2eShard.take('B(1.9.39) #2')) {
1754
+ total++;
1744
1755
  // session-workflow.md 파일이 init 시 생성
1745
1756
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wf2-'));
1746
1757
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1752,8 +1763,8 @@ total++;
1752
1763
  if (!ok) { failed++; if (fs.existsSync(wfFile)) console.log(fs.readFileSync(wfFile, 'utf8').slice(0, 300)); else console.log('파일 없음'); }
1753
1764
  }
1754
1765
 
1755
- total++;
1756
- {
1766
+ if (e2eShard.take('B(1.9.39) #3')) {
1767
+ total++;
1757
1768
  // AGENTS.md / CLAUDE.md에 session-workflow.md 참조 포함
1758
1769
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wf3-'));
1759
1770
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1764,8 +1775,8 @@ total++;
1764
1775
  if (!ok) { failed++; }
1765
1776
  }
1766
1777
 
1767
- total++;
1768
- {
1778
+ if (e2eShard.take('B(1.9.39) #4')) {
1779
+ total++;
1769
1780
  // drift check --auto-fix: critical 시 session close 자동 실행 (시뮬은 어려우니 옵션 인식만)
1770
1781
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-af-'));
1771
1782
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1779,8 +1790,8 @@ total++;
1779
1790
  }
1780
1791
 
1781
1792
  // 1.9.38 회귀: usage stats, task sync, drift reminder, drift skip learning
1782
- total++;
1783
- {
1793
+ if (e2eShard.take('B(1.9.38)')) {
1794
+ total++;
1784
1795
  // B. usage stats: 빈 상태 + 호출 후 카운터 증가
1785
1796
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-usage-'));
1786
1797
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1799,8 +1810,8 @@ total++;
1799
1810
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1800
1811
  }
1801
1812
 
1802
- total++;
1803
- {
1813
+ if (e2eShard.take('B(1.9.38) #2')) {
1814
+ total++;
1804
1815
  // C. task sync — TodoWrite JSON 임포트
1805
1816
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tasksync-'));
1806
1817
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1818,8 +1829,8 @@ total++;
1818
1829
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
1819
1830
  }
1820
1831
 
1821
- total++;
1822
- {
1832
+ if (e2eShard.take('B(1.9.38) #3')) {
1833
+ total++;
1823
1834
  // A. drift reminder 파일 자동 생성 (인공 stale 시뮬)
1824
1835
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rem-'));
1825
1836
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1840,8 +1851,8 @@ total++;
1840
1851
  if (!ok) { failed++; if (fs.existsSync(remPath)) console.log(fs.readFileSync(remPath, 'utf8').slice(0, 400)); else console.log('(reminder 파일 없음)'); }
1841
1852
  }
1842
1853
 
1843
- total++;
1844
- {
1854
+ if (e2eShard.take('B(1.9.38) #4')) {
1855
+ total++;
1845
1856
  // D. drift 학습 — --no-drift-check 5회 호출 후 임계 완화
1846
1857
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-learn-'));
1847
1858
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1856,8 +1867,8 @@ total++;
1856
1867
  }
1857
1868
 
1858
1869
  // 1.9.37 회귀: drift detection
1859
- total++;
1860
- {
1870
+ if (e2eShard.take('B(1.9.37)')) {
1871
+ total++;
1861
1872
  // drift check: 신규 init 직후 (메타파일은 fresh) → healthy 또는 attention
1862
1873
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-drift-'));
1863
1874
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1869,8 +1880,8 @@ total++;
1869
1880
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1870
1881
  }
1871
1882
 
1872
- total++;
1873
- {
1883
+ if (e2eShard.take('B(1.9.37) #2')) {
1884
+ total++;
1874
1885
  // drift check --json: 점수/신호 구조 검증
1875
1886
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-drift2-'));
1876
1887
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1887,8 +1898,8 @@ total++;
1887
1898
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1888
1899
  }
1889
1900
 
1890
- total++;
1891
- {
1901
+ if (e2eShard.take('B(1.9.37) #3')) {
1902
+ total++;
1892
1903
  // handoff 자동 drift 경고 — 인공 stale 시뮬 (session-handoff.md의 Last generated를 옛 날짜로)
1893
1904
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-drift3-'));
1894
1905
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1907,8 +1918,8 @@ total++;
1907
1918
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1908
1919
  }
1909
1920
 
1910
- total++;
1911
- {
1921
+ if (e2eShard.take('B(1.9.37) #4')) {
1922
+ total++;
1912
1923
  // LEERNESS_NO_DRIFT_CHECK=1: 자동 경고 스킵
1913
1924
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-drift4-'));
1914
1925
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -1928,8 +1939,8 @@ total++;
1928
1939
  }
1929
1940
 
1930
1941
  // 1.9.36 회귀: dispatch 권장 플래그 + bench + 작업 유형 추천
1931
- total++;
1932
- {
1942
+ if (e2eShard.take('B(1.9.36)')) {
1943
+ total++;
1933
1944
  // dispatch --write 시 agy --yolo 자동 추가
1934
1945
  const env = { ...process.env, LEERNESS_ENABLE_AGY: '1' };
1935
1946
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', '코드 분석해서 요약', '--to', 'agy', '--write'], { encoding: 'utf8', timeout: 15000, env });
@@ -1940,8 +1951,8 @@ total++;
1940
1951
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1941
1952
  }
1942
1953
 
1943
- total++;
1944
- {
1954
+ if (e2eShard.take('B(1.9.36) #2')) {
1955
+ total++;
1945
1956
  // dispatch read-only (기본) — --yolo/--dangerously 같은 위험 플래그 없음
1946
1957
  const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1' };
1947
1958
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', '번역해줘', '--to', 'claude'], { encoding: 'utf8', timeout: 15000, env });
@@ -1952,8 +1963,8 @@ total++;
1952
1963
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1953
1964
  }
1954
1965
 
1955
- total++;
1956
- {
1966
+ if (e2eShard.take('B(1.9.36) #3')) {
1967
+ total++;
1957
1968
  // 작업 유형 추천 — 비활성 CLI에도 추천 메시지 우선 출력
1958
1969
  const env = { ...process.env, LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_CLAUDE: '0' };
1959
1970
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', '번역해줘 한국어를 영어로', '--to', 'agy'], { encoding: 'utf8', timeout: 15000, env });
@@ -1963,8 +1974,8 @@ total++;
1963
1974
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
1964
1975
  }
1965
1976
 
1966
- total++;
1967
- {
1977
+ if (e2eShard.take('B(1.9.36) #4')) {
1978
+ total++;
1968
1979
  // bench 명령: ready CLI 없을 때 거부
1969
1980
  // 빌트인 provider가 늘어나도 상속 환경에서 실제 agent가 실행되지 않도록 레지스트리 전체를 끈다.
1970
1981
  // PATH 선두의 실행 마커는 disabled provider가 version probe조차 받지 않는지를 검증한다.
@@ -1983,8 +1994,8 @@ total++;
1983
1994
  if (!ok) { failed++; console.log(r.stdout.slice(0, 300)); }
1984
1995
  }
1985
1996
 
1986
- total++;
1987
- {
1997
+ if (e2eShard.take('B(1.9.36) #5')) {
1998
+ total++;
1988
1999
  // 사용법 메시지에 bench 포함
1989
2000
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'unknown'], { encoding: 'utf8', timeout: 10000 });
1990
2001
  const ok = r.status !== 0 && /bench/.test(r.stdout + r.stderr);
@@ -1993,8 +2004,8 @@ total++;
1993
2004
  }
1994
2005
 
1995
2006
  // 1.9.35 회귀: 5개 신규 기능
1996
- total++;
1997
- {
2007
+ if (e2eShard.take('B(1.9.35)')) {
2008
+ total++;
1998
2009
  // 개선#1: handoff 시 .leerness 부재 자동 경고
1999
2010
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-noinit-'));
2000
2011
  const r = cp.spawnSync(process.execPath, [CLI, 'handoff', tmpC], { encoding: 'utf8', timeout: 10000 });
@@ -2003,8 +2014,8 @@ total++;
2003
2014
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
2004
2015
  }
2005
2016
 
2006
- total++;
2007
- {
2017
+ if (e2eShard.take('B(1.9.35) #2')) {
2018
+ total++;
2008
2019
  // 개선#5: audit --fix 옵션 (플래그 인식만 검증, 실 fix는 통합 환경 필요)
2009
2020
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-fix-'));
2010
2021
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2014,8 +2025,8 @@ total++;
2014
2025
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
2015
2026
  }
2016
2027
 
2017
- total++;
2018
- {
2028
+ if (e2eShard.take('B(1.9.35) #3')) {
2029
+ total++;
2019
2030
  // 개선#3: contract verify
2020
2031
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-contract-'));
2021
2032
  const specFile = path.join(tmpDir, 'spec.md');
@@ -2033,8 +2044,8 @@ total++;
2033
2044
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
2034
2045
  }
2035
2046
 
2036
- total++;
2037
- {
2047
+ if (e2eShard.take('B(1.9.35) #4')) {
2048
+ total++;
2038
2049
  // 개선#2: reuse autodetect — src/*.js의 module.exports 스캔
2039
2050
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-autodetect-'));
2040
2051
  fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
@@ -2048,8 +2059,8 @@ total++;
2048
2059
  if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
2049
2060
  }
2050
2061
 
2051
- total++;
2052
- {
2062
+ if (e2eShard.take('B(1.9.35) #5')) {
2063
+ total++;
2053
2064
  // 개선#4: agents dispatch 안내문에 mtime/contract 안전 규칙 추가
2054
2065
  const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '1' };
2055
2066
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'dispatch', 'test task', '--to', 'claude'], { encoding: 'utf8', timeout: 15000, env });
@@ -2063,8 +2074,8 @@ total++;
2063
2074
  }
2064
2075
 
2065
2076
  // 1.9.34 회귀: 인터랙티브 multi-select (방향키/스페이스) — 비-TTY 폴백
2066
- total++;
2067
- {
2077
+ if (e2eShard.take('B(1.9.34)')) {
2078
+ total++;
2068
2079
  // 비-TTY에서는 _selectOne/_selectMany가 defaults 또는 첫 옵션 반환
2069
2080
  // → init이 --yes 없이도 비대화형이면 기본 스킬셋(recommended)로 진행
2070
2081
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-multi-'));
@@ -2077,8 +2088,8 @@ total++;
2077
2088
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2078
2089
  }
2079
2090
 
2080
- total++;
2081
- {
2091
+ if (e2eShard.take('B(1.9.34) #2')) {
2092
+ total++;
2082
2093
  // 1.9.34 배너 256색 그라데이션 — TTY 강제 + --banner (1.9.144+ 신규 슬로건)
2083
2094
  const r = cp.spawnSync(process.execPath, [CLI, '--version', '--banner'], { encoding: 'utf8', timeout: 10000 });
2084
2095
  const ok = r.status === 0
@@ -2090,8 +2101,8 @@ total++;
2090
2101
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2091
2102
  }
2092
2103
 
2093
- total++;
2094
- {
2104
+ if (e2eShard.take('B(1.9.34) #3')) {
2105
+ total++;
2095
2106
  // 1.9.34 LEERNESS_NO_INTERACTIVE=1: 구식 숫자 prompt 사용 (TTY 일 때만 의미 있음; 비-TTY는 어차피 --yes 폴백)
2096
2107
  // 검증: --no-interactive-select 플래그가 인식되고 에러 안 남
2097
2108
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-noint-'));
@@ -2101,8 +2112,8 @@ total++;
2101
2112
  if (!ok) { failed++; console.log(r.stdout.slice(0, 300)); }
2102
2113
  }
2103
2114
 
2104
- total++;
2105
- {
2115
+ if (e2eShard.take('B(1.9.33) #2')) {
2116
+ total++;
2106
2117
  // --no-stale-check / LEERNESS_NO_STALE_CHECK=1: 경고 스킵
2107
2118
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-stale2-'));
2108
2119
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended', '--no-stale-check'], { stdio: 'ignore', timeout: 30000 });
@@ -2121,8 +2132,8 @@ total++;
2121
2132
  }
2122
2133
 
2123
2134
  // 1.9.22 회귀: handoff --compact + orchestrate opt-in 정책 + llm-bench record
2124
- total++;
2125
- {
2135
+ if (e2eShard.take('B(1.9.22/1.36.255)')) {
2136
+ total++;
2126
2137
  // handoff --compact: 1줄 요약 출력
2127
2138
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-compact-'));
2128
2139
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2139,8 +2150,8 @@ total++;
2139
2150
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2140
2151
  }
2141
2152
 
2142
- total++;
2143
- {
2153
+ if (e2eShard.take('B(1.9.22)')) {
2154
+ total++;
2144
2155
  // orchestrate: LEERNESS_OLLAMA_BASE_URL 없으면 거부 (자동 적용 금지 정책)
2145
2156
  const tmpO = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-orch-'));
2146
2157
  cp.spawnSync(process.execPath, [CLI, 'init', tmpO, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2156,8 +2167,8 @@ total++;
2156
2167
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2157
2168
  }
2158
2169
 
2159
- total++;
2160
- {
2170
+ if (e2eShard.take('B(1.9.22) #2')) {
2171
+ total++;
2161
2172
  // orchestrate: .env 파일 자동 로드 (단, fake URL이라 실제 호출은 실패)
2162
2173
  const tmpE = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-orch-env-'));
2163
2174
  cp.spawnSync(process.execPath, [CLI, 'init', tmpE, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2171,8 +2182,8 @@ total++;
2171
2182
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2172
2183
  }
2173
2184
 
2174
- total++;
2175
- {
2185
+ if (e2eShard.take('B(1.9.22) #3')) {
2186
+ total++;
2176
2187
  // llm-bench record: 히스토리 누적
2177
2188
  const tmpL = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-llmb-'));
2178
2189
  cp.spawnSync(process.execPath, [CLI, 'init', tmpL, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2184,8 +2195,8 @@ total++;
2184
2195
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2185
2196
  }
2186
2197
 
2187
- total++;
2188
- {
2198
+ if (e2eShard.take('B(1.9.20) #2')) {
2199
+ total++;
2189
2200
  // jest 출력 파싱
2190
2201
  const tmpJ = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tparse-'));
2191
2202
  cp.spawnSync(process.execPath, [CLI, 'init', tmpJ, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2204,8 +2215,8 @@ total++;
2204
2215
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2205
2216
  }
2206
2217
 
2207
- total++;
2208
- {
2218
+ if (e2eShard.take('B(1.9.20) #3')) {
2219
+ total++;
2209
2220
  // mocha "N passing"
2210
2221
  const tmpM = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mocha-'));
2211
2222
  cp.spawnSync(process.execPath, [CLI, 'init', tmpM, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2222,8 +2233,8 @@ total++;
2222
2233
  if (!ok) { failed++; console.log(r.stdout.slice(0, 500)); }
2223
2234
  }
2224
2235
 
2225
- total++;
2226
- {
2236
+ if (e2eShard.take('B(1.9.20) #4')) {
2237
+ total++;
2227
2238
  // verify-code --bench: scripts.bench 자동 실행 + evidence 누적
2228
2239
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bench-'));
2229
2240
  cp.spawnSync(process.execPath, [CLI, 'init', tmpB, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2246,8 +2257,8 @@ total++;
2246
2257
  }
2247
2258
 
2248
2259
  // 1.9.15 회귀: brainstorm 라인번호 / --all-apps / --include
2249
- total++;
2250
- {
2260
+ if (e2eShard.take('B(1.9.15)')) {
2261
+ total++;
2251
2262
  const tmpL = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-line-'));
2252
2263
  cp.spawnSync(process.execPath, [CLI, 'init', tmpL, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
2253
2264
  fs.appendFileSync(path.join(tmpL, '.leerness/decisions.md'), '\n### 2026-05-13 — 캐시 정책 결정\n- Reason: rate limit 회피\n');
@@ -2257,8 +2268,8 @@ total++;
2257
2268
  console.log(ok ? '✓ B(1.9.15) brainstorm: 파일:라인 + 매치 필드 표시' : `✗ 1.9.15 brainstorm 위치 실패\n${r.stdout.slice(0,500)}`);
2258
2269
  if (!ok) failed++;
2259
2270
  }
2260
- total++;
2261
- {
2271
+ if (e2eShard.take('B(1.9.15) #2')) {
2272
+ total++;
2262
2273
  // --include 다중 경로 통합
2263
2274
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wsA-'));
2264
2275
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wsB-'));
@@ -2271,8 +2282,8 @@ total++;
2271
2282
  console.log(ok ? '✓ B(1.9.15) retro --include: 2개 통합' : '✗ 1.9.15 retro --include 실패');
2272
2283
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
2273
2284
  }
2274
- total++;
2275
- {
2285
+ if (e2eShard.take('B(1.9.15) #3')) {
2286
+ total++;
2276
2287
  // insights --include
2277
2288
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iA-'));
2278
2289
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iB-'));
@@ -2283,8 +2294,8 @@ total++;
2283
2294
  console.log(ok ? '✓ B(1.9.15) insights --include: 표 형식 통합' : '✗ 1.9.15 insights --include 실패');
2284
2295
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
2285
2296
  }
2286
- total++;
2287
- {
2297
+ if (e2eShard.take('B(1.9.15) #4')) {
2298
+ total++;
2288
2299
  // 잘못된 --include 경로 — warn 출력 + .leerness 있는 것만 처리
2289
2300
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bad-'));
2290
2301
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2296,8 +2307,8 @@ total++;
2296
2307
  }
2297
2308
 
2298
2309
  // 1.9.14 회귀: A(Template 제외) / B(word boundary) / C(planned 포함) / D(코드블록 템플릿)
2299
- total++;
2300
- {
2310
+ if (e2eShard.take('B(1.9.14-A)')) {
2311
+ total++;
2301
2312
  // A: init 직후 decisions.md의 Template이 결정으로 카운트되지 않아야 함
2302
2313
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-A-'));
2303
2314
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2306,8 +2317,8 @@ total++;
2306
2317
  console.log(ok ? '✓ B(1.9.14-A) Template 제외: 누적 결정 0건' : `✗ A 실패\n${r.stdout.slice(0, 500)}`);
2307
2318
  if (!ok) failed++;
2308
2319
  }
2309
- total++;
2310
- {
2320
+ if (e2eShard.take('B(1.9.14-B)')) {
2321
+ total++;
2311
2322
  // B: brainstorm 토큰 매칭 — "API"는 매치, "AP"는 부분 매치라 안 잡힘
2312
2323
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-B-'));
2313
2324
  cp.spawnSync(process.execPath, [CLI, 'init', tmpB, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2320,8 +2331,8 @@ total++;
2320
2331
  console.log(ok ? '✓ B(1.9.14-B) brainstorm word boundary: limit 매치 / lim 부분매치 안 잡힘' : `✗ B 실패\n${r1.stdout.slice(0, 200)}\n${r2.stdout.slice(0, 200)}`);
2321
2332
  if (!ok) failed++;
2322
2333
  }
2323
- total++;
2324
- {
2334
+ if (e2eShard.take('B(1.9.14-C)')) {
2335
+ total++;
2325
2336
  // C: retro 다음 우선 작업에 planned 포함
2326
2337
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-C-'));
2327
2338
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2332,8 +2343,8 @@ total++;
2332
2343
  console.log(ok ? '✓ B(1.9.14-C) retro 다음 우선 작업에 planned 포함' : '✗ C 실패');
2333
2344
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
2334
2345
  }
2335
- total++;
2336
- {
2346
+ if (e2eShard.take('B(1.9.14-D)')) {
2347
+ total++;
2337
2348
  // D: init decisions.md가 ```md 코드블록으로 감싸짐
2338
2349
  const tmpD = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-D-'));
2339
2350
  cp.spawnSync(process.execPath, [CLI, 'init', tmpD, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2344,8 +2355,8 @@ total++;
2344
2355
  }
2345
2356
 
2346
2357
  // 1.9.13: retro / insights / brainstorm
2347
- total++;
2348
- {
2358
+ if (e2eShard.take('B(1.9.13)')) {
2359
+ total++;
2349
2360
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-retro-'));
2350
2361
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
2351
2362
  cp.spawnSync(process.execPath, [CLI, 'plan', 'add', '캐시 helper', '--status', 'done', '--path', tmpR], { stdio: 'ignore', timeout: 10000 });
@@ -2360,8 +2371,8 @@ total++;
2360
2371
  if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
2361
2372
  }
2362
2373
 
2363
- total++;
2364
- {
2374
+ if (e2eShard.take('B(1.9.13) #2')) {
2375
+ total++;
2365
2376
  const tmpI = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ins-'));
2366
2377
  cp.spawnSync(process.execPath, [CLI, 'init', tmpI, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
2367
2378
  const r = cp.spawnSync(process.execPath, [CLI, 'insights', tmpI], { encoding: 'utf8', timeout: 15000 });
@@ -2370,8 +2381,8 @@ total++;
2370
2381
  if (!ok) failed++;
2371
2382
  }
2372
2383
 
2373
- total++;
2374
- {
2384
+ if (e2eShard.take('B(1.9.13) #3')) {
2385
+ total++;
2375
2386
  const tmpB = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-brain-'));
2376
2387
  cp.spawnSync(process.execPath, [CLI, 'init', tmpB, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
2377
2388
  fs.appendFileSync(path.join(tmpB, '.leerness/decisions.md'), `\n### 2026-05-13 — 캐시 차등 TTL 결정\n- Reason: open-meteo 응답 최적화\n`);
@@ -2382,8 +2393,8 @@ total++;
2382
2393
  if (!ok) { failed++; console.log(r.stdout.slice(0, 600)); }
2383
2394
  }
2384
2395
 
2385
- total++;
2386
- {
2396
+ if (e2eShard.take('B(1.9.13) #4')) {
2397
+ total++;
2387
2398
  // session close 한 줄 요약 자동 출력
2388
2399
  const tmpS = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-summary-'));
2389
2400
  cp.spawnSync(process.execPath, [CLI, 'init', tmpS, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2393,8 +2404,8 @@ total++;
2393
2404
  if (!ok) { failed++; console.log(r.stdout.slice(-600)); }
2394
2405
  }
2395
2406
 
2396
- total++;
2397
- {
2407
+ if (e2eShard.take('B(1.9.13) #5')) {
2408
+ total++;
2398
2409
  // 5세션 마일스톤 — 자동 깊은 회고
2399
2410
  const tmpD = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-deep-'));
2400
2411
  cp.spawnSync(process.execPath, [CLI, 'init', tmpD, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
@@ -2410,8 +2421,8 @@ total++;
2410
2421
  // 1.9.284: roadmap 전용 테스트 블록 — roadmap 생성 일시 ON (이후 다시 OFF)
2411
2422
  process.env.LEERNESS_NO_AUTO_ROADMAP = '0';
2412
2423
  // 1.9.12: auto roadmap — install 직후 자동 생성
2413
- total++;
2414
- {
2424
+ if (e2eShard.take('B(1.9.12)')) {
2425
+ total++;
2415
2426
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auto1-'));
2416
2427
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2417
2428
  const ok = fs.existsSync(path.join(tmpA, 'leerness.html'));
@@ -2420,8 +2431,8 @@ total++;
2420
2431
  }
2421
2432
 
2422
2433
  // 1.9.12: session close 후 자동 갱신
2423
- total++;
2424
- {
2434
+ if (e2eShard.take('B(1.9.12) #2')) {
2435
+ total++;
2425
2436
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auto2-'));
2426
2437
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2427
2438
  // 첫 mtime 캡처
@@ -2439,8 +2450,8 @@ total++;
2439
2450
  }
2440
2451
 
2441
2452
  // 1.9.12: roadmap auto off → 더 이상 자동 갱신 안 함
2442
- total++;
2443
- {
2453
+ if (e2eShard.take('B(1.9.12) #3')) {
2454
+ total++;
2444
2455
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auto3-'));
2445
2456
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2446
2457
  cp.spawnSync(process.execPath, [CLI, 'roadmap', 'auto', 'off', '--path', tmpA], { stdio: 'ignore' });
@@ -2456,8 +2467,8 @@ total++;
2456
2467
  }
2457
2468
 
2458
2469
  // 1.9.12: --on-every-change 옵트인 시 task add만으로 갱신
2459
- total++;
2460
- {
2470
+ if (e2eShard.take('B(1.9.12) #4')) {
2471
+ total++;
2461
2472
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auto4-'));
2462
2473
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2463
2474
  cp.spawnSync(process.execPath, [CLI, 'roadmap', 'auto', 'on', '--on-every-change', '--path', tmpA], { stdio: 'ignore' });
@@ -2474,8 +2485,8 @@ total++;
2474
2485
  process.env.LEERNESS_NO_AUTO_ROADMAP = '1';
2475
2486
 
2476
2487
  // 1.9.12: status 출력
2477
- total++;
2478
- {
2488
+ if (e2eShard.take('B(1.9.12) #5')) {
2489
+ total++;
2479
2490
  const tmpA = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auto5-'));
2480
2491
  cp.spawnSync(process.execPath, [CLI, 'init', tmpA, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2481
2492
  const r = cp.spawnSync(process.execPath, [CLI, 'roadmap', 'auto', 'status', '--path', tmpA], { encoding: 'utf8' });
@@ -2485,8 +2496,8 @@ total++;
2485
2496
  }
2486
2497
 
2487
2498
  // 1.9.11: roadmap 명령 통합 + 화이트보드/토큰/중앙정렬 회귀
2488
- total++;
2489
- {
2499
+ if (e2eShard.take('B(1.9.11)')) {
2500
+ total++;
2490
2501
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rm-'));
2491
2502
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2492
2503
  const r = cp.spawnSync(process.execPath, [CLI, 'roadmap', tmpR], { encoding: 'utf8' });
@@ -2495,8 +2506,8 @@ total++;
2495
2506
  console.log(ok ? '✓ B(1.9.11) roadmap: 명령 + 파일 생성' : `✗ roadmap 실패\n${r.stdout}\n${r.stderr}`);
2496
2507
  if (!ok) failed++;
2497
2508
  }
2498
- total++;
2499
- {
2509
+ if (e2eShard.take('B(1.9.11) #2')) {
2510
+ total++;
2500
2511
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rm2-'));
2501
2512
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2502
2513
  cp.spawnSync(process.execPath, [CLI, 'roadmap', tmpR], { stdio: 'ignore' });
@@ -2505,8 +2516,8 @@ total++;
2505
2516
  console.log(ok ? '✓ B(1.9.11) roadmap: 화이트보드 (panning/zoom JS)' : '✗ 화이트보드 부재');
2506
2517
  if (!ok) failed++;
2507
2518
  }
2508
- total++;
2509
- {
2519
+ if (e2eShard.take('B(1.9.11) #3')) {
2520
+ total++;
2510
2521
  // 사용자 design-system 토큰 주입
2511
2522
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rm3-'));
2512
2523
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
@@ -2519,8 +2530,8 @@ total++;
2519
2530
  console.log(ok ? '✓ B(1.9.11) roadmap: design-system 토큰 자동 주입' : '✗ 토큰 주입 실패');
2520
2531
  if (!ok) failed++;
2521
2532
  }
2522
- total++;
2523
- {
2533
+ if (e2eShard.take('B(1.9.11) #4')) {
2534
+ total++;
2524
2535
  // recommended에 project-roadmap-generator 자동 포함
2525
2536
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rm4-'));
2526
2537
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
@@ -2532,8 +2543,8 @@ total++;
2532
2543
 
2533
2544
  // 1.9.10 A: skillpack 동적 로드 (LEERNESS_SKILLPACK_PATH로 시뮬)
2534
2545
  // 1.36.55 (외부감사 이식성): 형제 디렉토리 가정 제거 — 없으면 최소 catalog.json 픽스처를 테스트가 직접 생성 (자기완결)
2535
- total++;
2536
- {
2546
+ if (e2eShard.take('B(1.9.10)')) {
2547
+ total++;
2537
2548
  let skillpackDir = path.resolve(__dirname, '..', '..', 'leerness-skillpack');
2538
2549
  if (!fs.existsSync(path.join(skillpackDir, 'catalog.json'))) {
2539
2550
  skillpackDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-spfix-'));
@@ -2551,8 +2562,8 @@ total++;
2551
2562
  if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
2552
2563
  }
2553
2564
  // 1.9.10 A: skillpack 없을 때 builtin fallback
2554
- total++;
2555
- {
2565
+ if (e2eShard.take('B(1.9.10) #2')) {
2566
+ total++;
2556
2567
  const r = cp.spawnSync(process.execPath, [CLI, 'skill', 'list', '--path', tmp], {
2557
2568
  encoding: 'utf8',
2558
2569
  env: Object.assign({}, process.env, { LEERNESS_SKILLPACK_PATH: '' })
@@ -2563,8 +2574,8 @@ total++;
2563
2574
  }
2564
2575
 
2565
2576
  // 1.9.10 B: detectGitRemote (가짜 git remote 시뮬은 어려움 — 실제 git 명령으로 확인)
2566
- total++;
2567
- {
2577
+ if (e2eShard.take('B(1.9.10) #3')) {
2578
+ total++;
2568
2579
  // tmp는 git init이 없음 → detectGitRemote는 null → publish 호출 시 'Git remote: 없음' 출력
2569
2580
  // 시뮬: tmp에 git init + remote add
2570
2581
  gitSpawn(['init'], { cwd: tmp, encoding: 'utf8' });
@@ -2580,8 +2591,8 @@ total++;
2580
2591
  }
2581
2592
 
2582
2593
  // 1.9.8: rule add/list/pause/resume/remove
2583
- total++;
2584
- {
2594
+ if (e2eShard.take('B(1.9.8)')) {
2595
+ total++;
2585
2596
  const r1 = cp.spawnSync(process.execPath, [CLI, 'rule', 'add', '매 업데이트마다 버전 patch bump', '--trigger', 'every-update', '--path', tmp], { encoding: 'utf8' });
2586
2597
  const r2 = cp.spawnSync(process.execPath, [CLI, 'rule', 'add', '매 업데이트마다 패치노트 추가', '--trigger', 'every-update', '--path', tmp], { encoding: 'utf8' });
2587
2598
  const r3 = cp.spawnSync(process.execPath, [CLI, 'rule', 'add', '세션 종료마다 배포', '--trigger', 'session-close', '--path', tmp], { encoding: 'utf8' });
@@ -2590,8 +2601,8 @@ total++;
2590
2601
  console.log(ok ? '✓ B(1.9.8) rule add/list: 3개 등록' : '✗ B(1.9.8) rule add/list 실패');
2591
2602
  if (!ok) failed++;
2592
2603
  }
2593
- total++;
2594
- {
2604
+ if (e2eShard.take('B(1.9.8) #2')) {
2605
+ total++;
2595
2606
  // pause + handoff에서 paused는 안 보여야 함
2596
2607
  cp.spawnSync(process.execPath, [CLI, 'rule', 'pause', 'R-0001', '--path', tmp], { encoding: 'utf8' });
2597
2608
  const hr = cp.spawnSync(process.execPath, [CLI, 'handoff', tmp], { encoding: 'utf8' });
@@ -2600,8 +2611,8 @@ total++;
2600
2611
  if (!ok) { failed++; console.log(hr.stdout); }
2601
2612
  cp.spawnSync(process.execPath, [CLI, 'rule', 'resume', 'R-0001', '--path', tmp], { encoding: 'utf8' });
2602
2613
  }
2603
- total++;
2604
- {
2614
+ if (e2eShard.take('B(1.9.8) #3')) {
2615
+ total++;
2605
2616
  // rule stop / resume-all
2606
2617
  cp.spawnSync(process.execPath, [CLI, 'rule', 'stop', '--path', tmp], { encoding: 'utf8' });
2607
2618
  const rl = cp.spawnSync(process.execPath, [CLI, 'rule', 'list', '--path', tmp], { encoding: 'utf8' });
@@ -2614,8 +2625,8 @@ total++;
2614
2625
  }
2615
2626
 
2616
2627
  // 1.9.8: release bump
2617
- total++;
2618
- {
2628
+ if (e2eShard.take('B(1.9.8) #4')) {
2629
+ total++;
2619
2630
  const tmpR = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rel-'));
2620
2631
  cp.spawnSync(process.execPath, [CLI, 'init', tmpR, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2621
2632
  fs.writeFileSync(path.join(tmpR, 'package.json'), JSON.stringify({ name: 't', version: '1.0.0' }));
@@ -2633,8 +2644,8 @@ total++;
2633
2644
  }
2634
2645
 
2635
2646
  // T-0122: --patch는 경고 없이 수용하고, leerness 패키지의 package/bin/README 버전 표면을 함께 갱신한다.
2636
- total++;
2637
- {
2647
+ if (e2eShard.take('B(T-0122)')) {
2648
+ total++;
2638
2649
  const probe = cp.spawnSync(process.execPath, [path.join(__dirname, 'release-bump-probe.js')], {
2639
2650
  cwd: path.resolve(__dirname, '..'), encoding: 'utf8', timeout: 30000
2640
2651
  });
@@ -2644,8 +2655,8 @@ total++;
2644
2655
  }
2645
2656
 
2646
2657
  // 1.9.8: release note → CHANGELOG.md 자동 갱신
2647
- total++;
2648
- {
2658
+ if (e2eShard.take('B(1.9.8) #5')) {
2659
+ total++;
2649
2660
  const tmpN = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-note-'));
2650
2661
  cp.spawnSync(process.execPath, [CLI, 'init', tmpN, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2651
2662
  fs.writeFileSync(path.join(tmpN, 'package.json'), JSON.stringify({ name: 't', version: '0.1.0' }));
@@ -2657,8 +2668,8 @@ total++;
2657
2668
  }
2658
2669
 
2659
2670
  // 1.9.8: session close가 rule verification 보고
2660
- total++;
2661
- {
2671
+ if (e2eShard.take('pass')) {
2672
+ total++;
2662
2673
  // tmp는 위에서 rule 3개 등록됨
2663
2674
  // package.json 만들기 + 버전 변경 시뮬 (rule R-0001은 every-update 버전 룰)
2664
2675
  fs.writeFileSync(path.join(tmp, 'package.json'), JSON.stringify({ name: 'tmp-e2e', version: '0.1.0' }));
@@ -2680,8 +2691,8 @@ total++;
2680
2691
  }
2681
2692
 
2682
2693
  // 1.9.7 A: verify-code — 가짜 package.json + 통과 시나리오
2683
- total++;
2684
- {
2694
+ if (e2eShard.take('B(1.9.7-A)')) {
2695
+ total++;
2685
2696
  const tmpV = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vc-'));
2686
2697
  cp.spawnSync(process.execPath, [CLI, 'init', tmpV, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2687
2698
  fs.writeFileSync(path.join(tmpV, 'package.json'), JSON.stringify({ name: 't', version: '0.0.1', scripts: { test: 'node -e "console.log(\\"OK\\");process.exit(0)"' } }));
@@ -2693,8 +2704,8 @@ total++;
2693
2704
  }
2694
2705
 
2695
2706
  // 1.9.7 A: verify-code — 실패 시나리오
2696
- total++;
2697
- {
2707
+ if (e2eShard.take('B(1.9.7-A) #2')) {
2708
+ total++;
2698
2709
  const tmpV2 = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vc2-'));
2699
2710
  cp.spawnSync(process.execPath, [CLI, 'init', tmpV2, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2700
2711
  fs.writeFileSync(path.join(tmpV2, 'package.json'), JSON.stringify({ name: 't', version: '0.0.1', scripts: { test: 'node -e "process.exit(2)"' } }));
@@ -2705,8 +2716,8 @@ total++;
2705
2716
  }
2706
2717
 
2707
2718
  // 1.9.7 B: lessons — decisions/evidence에 시드 후 query로 회수
2708
- total++;
2709
- {
2719
+ if (e2eShard.take('B(1.9.7-B)')) {
2720
+ total++;
2710
2721
  fs.appendFileSync(path.join(tmp, '.leerness/decisions.md'), `\n### 2026-05-08 — Decision: 캐시 차등 TTL 도입\n- Reason: 단일 5분 TTL이 daily 데이터에 비효율\n- Impact: open-meteo 응답 캐시 적중률 ↑\n`);
2711
2722
  fs.appendFileSync(path.join(tmp, '.leerness/review-evidence.md'), `\n## 2026-05-08 e2e\n✗ 캐시 키 불안정 — 좌표 정규화 부재 (롤백 후 fix)\n`);
2712
2723
  const r = cp.spawnSync(process.execPath, [CLI, 'lessons', '--query', '캐시', '--path', tmp], { encoding: 'utf8' });
@@ -2716,8 +2727,8 @@ total++;
2716
2727
  }
2717
2728
 
2718
2729
  // 1.9.7 B: guide가 lessons를 자동 통합
2719
- total++;
2720
- {
2730
+ if (e2eShard.take('B(1.9.7-B) #2')) {
2731
+ total++;
2721
2732
  const r = cp.spawnSync(process.execPath, [CLI, 'guide', 'src/components/Card.html', '--path', tmp], { encoding: 'utf8' });
2722
2733
  const ok = r.status === 0 && /## 4\. Lessons/.test(r.stdout);
2723
2734
  console.log(ok ? '✓ B(1.9.7-B) guide: lessons 섹션 자동 추가' : '✗ B guide 통합 실패');
@@ -2725,8 +2736,8 @@ total++;
2725
2736
  }
2726
2737
 
2727
2738
  // 1.9.7 C: lazy detect --auto-track
2728
- total++;
2729
- {
2739
+ if (e2eShard.take('B(1.9.7-C)')) {
2740
+ total++;
2730
2741
  const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-c-'));
2731
2742
  cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore' });
2732
2743
  // 의도된 진짜 TODO (주석)
@@ -2746,8 +2757,8 @@ total++;
2746
2757
  }
2747
2758
 
2748
2759
  // 1.9.7 C: 같은 TODO 재실행 시 known-todos가 적용되어 newTodos 0
2749
- total++;
2750
- {
2760
+ if (e2eShard.take('B(1.9.7-C) #2')) {
2761
+ total++;
2751
2762
  // 위 시나리오 이어서 — known-todos가 있으므로 새 TODO=0이어야 함
2752
2763
  // 별도 새 tmp로 재현 (tmpC는 위에서 자동 등록됐으니 same dir에서 다시 호출)
2753
2764
  // 위 tmpC는 already auto-tracked
@@ -2768,8 +2779,8 @@ total++;
2768
2779
  }
2769
2780
 
2770
2781
  // 1.9.6 회귀: task relink — 인위적 link 손실 → 자동 매칭 제안 + --apply 자동 복구
2771
- total++;
2772
- {
2782
+ if (e2eShard.take('B(1.9.6)')) {
2783
+ total++;
2773
2784
  // plan.md에 새 milestone 추가, progress엔 link 없는 비슷한 row 추가
2774
2785
  const planPath_ = path.join(tmp, '.leerness/plan.md');
2775
2786
  fs.appendFileSync(planPath_,
@@ -2790,8 +2801,8 @@ total++;
2790
2801
  console.log(okSuggest ? '✓ B(1.9.6) task relink 제안: 2개 매칭 발견 (--dry-run 도 같은 제안)' : `✗ B(1.9.6) 제안 실패\n${r1.stdout}\n--dry-run: exit=${r1d.status} ${r1d.stdout}${r1d.stderr}`);
2791
2802
  if (!okSuggest) failed++;
2792
2803
  }
2793
- total++;
2794
- {
2804
+ if (e2eShard.take('B(1.9.6) #2')) {
2805
+ total++;
2795
2806
  // --apply
2796
2807
  const r2 = cp.spawnSync(process.execPath, [CLI, 'task', 'relink', '--apply', '--path', tmp], { encoding: 'utf8' });
2797
2808
  const tracker = fs.readFileSync(path.join(tmp, '.leerness/progress-tracker.md'), 'utf8');
@@ -2799,8 +2810,8 @@ total++;
2799
2810
  console.log(okApply ? '✓ B(1.9.6) task relink --apply: 자동 복구' : '✗ B(1.9.6) --apply 실패');
2800
2811
  if (!okApply) failed++;
2801
2812
  }
2802
- total++;
2803
- {
2813
+ if (e2eShard.take('B(1.9.6) #3')) {
2814
+ total++;
2804
2815
  // audit이 task relink 안내를 출력하는지 (이번엔 link 복구 후라 미연결 milestone 없을 것)
2805
2816
  // 일부러 새 milestone 추가 후 audit
2806
2817
  fs.appendFileSync(path.join(tmp, '.leerness/plan.md'),
@@ -2812,8 +2823,8 @@ total++;
2812
2823
  }
2813
2824
 
2814
2825
  // 1.9.5 G 회귀: impact medium 카테고리 — 동적 path 패턴
2815
- total++;
2816
- {
2826
+ if (e2eShard.take('B(G)')) {
2827
+ total++;
2817
2828
  // builder.js에서 path.join + readFileSync + Card.html을 동적 사용
2818
2829
  fs.writeFileSync(path.join(tmp, 'src/builder.js'),
2819
2830
  `const fs = require('fs'); const path = require('path');\n` +
@@ -2826,8 +2837,8 @@ total++;
2826
2837
  }
2827
2838
 
2828
2839
  // 1.9.4 회귀: --fail-on-violation cross-platform 종료
2829
- total++;
2830
- {
2840
+ if (e2eShard.take('B(B)')) {
2841
+ total++;
2831
2842
  fs.appendFileSync(path.join(tmp, 'src/pages/home.html'), '\n<style>.x{color:#cafe00;}</style>\n');
2832
2843
  const r = cp.spawnSync(process.execPath, [CLI, 'ui', 'consistency', tmp, '--fail-on-violation'], { encoding: 'utf8' });
2833
2844
  const ok = r.status === 1;
@@ -2836,8 +2847,8 @@ total++;
2836
2847
  }
2837
2848
 
2838
2849
  // 1.9.4 회귀: lazy detect string literal 무시
2839
- total++;
2840
- {
2850
+ if (e2eShard.take('B(C)')) {
2851
+ total++;
2841
2852
  // false positive 시드: TODO 단어가 string literal 안에 있는 코드
2842
2853
  fs.writeFileSync(path.join(tmp, 'src/regex-helper.js'), `module.exports = { TODO_RE: /\\bTODO\\b/g, label: 'TODO list' };\n`);
2843
2854
  // 다른 한편 진짜 TODO 주석
@@ -2853,8 +2864,8 @@ total++;
2853
2864
  }
2854
2865
 
2855
2866
  // 1.9.4 회귀: task fix-evidence 표시
2856
- total++;
2857
- {
2867
+ if (e2eShard.take('B(D)')) {
2868
+ total++;
2858
2869
  // T-0001 evidence를 placeholder로 (test before가 'review-evidence:e2e' 였음 → 'user-request'로 바꿈)
2859
2870
  const trackerPath = path.join(tmp, '.leerness/progress-tracker.md');
2860
2871
  let cur = fs.readFileSync(trackerPath, 'utf8');
@@ -2867,8 +2878,8 @@ total++;
2867
2878
  }
2868
2879
 
2869
2880
  // 1.9.4 회귀: --set 일괄 갱신
2870
- total++;
2871
- {
2881
+ if (e2eShard.take('B(D2)')) {
2882
+ total++;
2872
2883
  // T-0001 evidence를 plan:M-0002 링크 포함한 placeholder로 (1.9.5 link 보존 검증용 시드)
2873
2884
  const trackerPath2 = path.join(tmp, '.leerness/progress-tracker.md');
2874
2885
  let cur = fs.readFileSync(trackerPath2, 'utf8');
@@ -2882,8 +2893,8 @@ total++;
2882
2893
  }
2883
2894
 
2884
2895
  // 1.9.5 F 회귀: --set 시 plan:M-XXXX 링크 자동 보존
2885
- total++;
2886
- {
2896
+ if (e2eShard.take('B(F)')) {
2897
+ total++;
2887
2898
  const tracker = fs.readFileSync(path.join(tmp, '.leerness/progress-tracker.md'), 'utf8');
2888
2899
  const ok = /npm test 통과 \(e2e\) \(plan:M-0002\)/.test(tracker);
2889
2900
  console.log(ok ? '✓ B(F) fix-evidence --set: link 자동 보존' : `✗ B(F) link 손실\n${tracker}`);
@@ -2891,8 +2902,8 @@ total++;
2891
2902
  }
2892
2903
 
2893
2904
  // 1.9.5 F neg: --no-preserve-link
2894
- total++;
2895
- {
2905
+ if (e2eShard.take('B(F neg)')) {
2906
+ total++;
2896
2907
  // T-0002 같은 row를 placeholder로 시드
2897
2908
  const trackerPath3 = path.join(tmp, '.leerness/progress-tracker.md');
2898
2909
  let cur = fs.readFileSync(trackerPath3, 'utf8');
@@ -2907,8 +2918,8 @@ total++;
2907
2918
  }
2908
2919
 
2909
2920
  // 1.9.4 회귀: .leerness-skip-dirs 적용
2910
- total++;
2911
- {
2921
+ if (e2eShard.take('B(E)')) {
2922
+ total++;
2912
2923
  fs.mkdirSync(path.join(tmp, '_devspace'), { recursive: true });
2913
2924
  fs.writeFileSync(path.join(tmp, '_devspace/secret-config.js'), `const k = "ghp_${'a1B2'.repeat(9)}";\n`);
2914
2925
  fs.writeFileSync(path.join(tmp, '.leerness-skip-dirs'), '_devspace/\n# 주석은 무시\n');
@@ -2927,8 +2938,8 @@ run('--version', ['--version']);
2927
2938
  run('--help', ['--help']);
2928
2939
 
2929
2940
  // 1.9.269 회귀 (UR-0022): 빈 디렉토리 신규 init + auto → OS 시스템 언어로 .leerness/LANGUAGE 결정
2930
- total++;
2931
- {
2941
+ if (e2eShard.take('B(1.9.269)')) {
2942
+ total++;
2932
2943
  const tmpEn = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lang-en-'));
2933
2944
  const tmpKo = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lang-ko-'));
2934
2945
  // LANG 명시 (POSIX) — Windows 에서도 _detectSystemLang 이 LANG 우선 읽음
@@ -2945,8 +2956,8 @@ total++;
2945
2956
  }
2946
2957
 
2947
2958
  // 1.9.270 회귀: agent roles — 모델별 역할 부여 (사용자 명시)
2948
- total++;
2949
- {
2959
+ if (e2eShard.take('B(1.9.270)')) {
2960
+ total++;
2950
2961
  const tmpRole = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-roles-'));
2951
2962
  // set (한국어 별칭) + model
2952
2963
  const rSet = cp.spawnSync(process.execPath, [CLI, 'roles', 'set', '코딩', '--provider', 'codex', '--model', 'gpt-5.5', '--path', tmpRole, '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -2971,8 +2982,8 @@ total++;
2971
2982
  }
2972
2983
 
2973
2984
  // 1.9.272 회귀: capabilities — 권한/보안 표면 공개 (GPT-5.5 리뷰 반영)
2974
- total++;
2975
- {
2985
+ if (e2eShard.take('B(1.9.272)')) {
2986
+ total++;
2976
2987
  const rJson = cp.spawnSync(process.execPath, [CLI, 'capabilities', '--json'], { encoding: 'utf8', timeout: 15000 });
2977
2988
  let jsonOk = false;
2978
2989
  try {
@@ -2989,8 +3000,8 @@ total++;
2989
3000
  }
2990
3001
 
2991
3002
  // 1.9.273 회귀 (UR-0027): 빠른 테스트 인프라 존재 검증 (smoke.js + test:fast script). 실제 smoke 실행은 별도(npm run test:fast).
2992
- total++;
2993
- {
3003
+ if (e2eShard.take('B(1.9.273)')) {
3004
+ total++;
2994
3005
  const smokePath = path.resolve(__dirname, 'smoke.js');
2995
3006
  const smokeExists = fs.existsSync(smokePath);
2996
3007
  // 구문 유효성 (node --check)
@@ -3006,8 +3017,8 @@ total++;
3006
3017
  }
3007
3018
 
3008
3019
  // 1.9.274 회귀 (UR-0025 1단계): lib/ 모듈 분리 — pure-utils.js 존재 + require + 7종 export + 패키지 files 포함.
3009
- total++;
3010
- {
3020
+ if (e2eShard.take('B(1.9.274/283)')) {
3021
+ total++;
3011
3022
  const libPath = path.resolve(__dirname, '..', 'lib', 'pure-utils.js');
3012
3023
  const libExists = fs.existsSync(libPath);
3013
3024
  let exportsOk = false;
@@ -3030,8 +3041,8 @@ total++;
3030
3041
  }
3031
3042
 
3032
3043
  // 1.9.275 회귀 (UR-0026): release channel — 안정/실험 채널 정책 (npm dist-tag)
3033
- total++;
3034
- {
3044
+ if (e2eShard.take('B(1.9.275)')) {
3045
+ total++;
3035
3046
  const r = cp.spawnSync(process.execPath, [CLI, 'release', 'channel', tmp, '--json', '--offline'], { encoding: 'utf8', timeout: 15000 });
3036
3047
  let jsonOk = false;
3037
3048
  try { const j = JSON.parse(r.stdout); jsonOk = j.defaultPublishTag === 'latest' && j.policy && j.policy.stable === 'latest' && j.policy.experimental === 'next'; } catch {}
@@ -3044,8 +3055,8 @@ total++;
3044
3055
  }
3045
3056
 
3046
3057
  // 1.9.276 회귀 (GPT-5.5 2차 리뷰): init --dry-run / --minimal / --no-env
3047
- total++;
3048
- {
3058
+ if (e2eShard.take('B(1.9.276)')) {
3059
+ total++;
3049
3060
  // --dry-run: 파일 0개 생성 + 요약 출력
3050
3061
  const dDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-dry-'));
3051
3062
  const rDry = cp.spawnSync(process.execPath, [CLI, 'init', dDir, '--dry-run', '--yes'], { encoding: 'utf8', timeout: 30000 });
@@ -3070,8 +3081,8 @@ total++;
3070
3081
  }
3071
3082
 
3072
3083
  // 1.9.278 회귀 (UR-0032): .leerness/ 상태 스키마 라이프사이클 (start→record→verify→handoff→show)
3073
- total++;
3074
- {
3084
+ if (e2eShard.take('B(1.9.278)')) {
3085
+ total++;
3075
3086
  const sDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-state-'));
3076
3087
  const run = (a) => cp.spawnSync(process.execPath, [CLI, 'state', ...a, '--path', sDir], { encoding: 'utf8', timeout: 15000 });
3077
3088
  run(['start', 'API 구현', '--agent', 'claude', '--model', 'claude-opus-4-7', '--task', 'T-0001']);
@@ -3102,8 +3113,8 @@ total++;
3102
3113
  }
3103
3114
 
3104
3115
  // 1.9.279 회귀 (UR-0031): 상태 substrate MCP 시맨틱 verb (state_start → state_show 라운드트립)
3105
- total++;
3106
- {
3116
+ if (e2eShard.take('B(1.9.279)')) {
3117
+ total++;
3107
3118
  const mDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mcpstate-'));
3108
3119
  const mcpCall = (req) => {
3109
3120
  const r = cp.spawnSync(process.execPath, [CLI, 'mcp', 'serve'], { encoding: 'utf8', timeout: 10000, input: JSON.stringify(req) + '\n' });
@@ -3124,8 +3135,8 @@ total++;
3124
3135
  }
3125
3136
 
3126
3137
  // 1.9.280 회귀 (UR-0033): leerness adapter — 도구별 선택 생성 + .mcp.json
3127
- total++;
3128
- {
3138
+ if (e2eShard.take('B(1.9.280)')) {
3139
+ total++;
3129
3140
  const aDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-adapter-'));
3130
3141
  cp.spawnSync(process.execPath, [CLI, 'init', aDir, '--minimal', '--no-env', '--yes'], { encoding: 'utf8', timeout: 30000 });
3131
3142
  // adapter cursor: .cursor + .mcp.json 생성, .claude 미생성(minimal이라 commands는 있으나 cursor adapter는 안 건드림)
@@ -3150,8 +3161,8 @@ total++;
3150
3161
  }
3151
3162
 
3152
3163
  // 1.9.281 회귀 (UR-0034): 권한 등급 policy (show/set/check + enforce)
3153
- total++;
3154
- {
3164
+ if (e2eShard.take('B(1.9.281)')) {
3165
+ total++;
3155
3166
  const pDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-policy-'));
3156
3167
  const run = (a) => cp.spawnSync(process.execPath, [CLI, 'policy', ...a, '--path', pDir], { encoding: 'utf8', timeout: 15000 });
3157
3168
  // 기본: enforce OFF + 8 등급
@@ -3172,8 +3183,8 @@ total++;
3172
3183
  }
3173
3184
 
3174
3185
  // 1.9.282 회귀 (UR-0035): AGENTS.md 정적 vs 동적(leerness) 역할 경계 포지셔닝
3175
- total++;
3176
- {
3186
+ if (e2eShard.take('B(1.9.282)')) {
3187
+ total++;
3177
3188
  const gDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-pos-'));
3178
3189
  cp.spawnSync(process.execPath, [CLI, 'init', gDir, '--minimal', '--no-env', '--yes'], { encoding: 'utf8', timeout: 30000 });
3179
3190
  let ok = false;
@@ -3186,8 +3197,8 @@ total++;
3186
3197
  }
3187
3198
 
3188
3199
  // 1.9.285 회귀 (UR-0023): reuse-check — 외부 OSS 빌드 vs 재사용 게이트
3189
- total++;
3190
- {
3200
+ if (e2eShard.take('B(1.9.285)')) {
3201
+ total++;
3191
3202
  const r1 = cp.spawnSync(process.execPath, [CLI, 'reuse-check', 'JWT 인증 로그인', '--json'], { encoding: 'utf8', timeout: 15000 });
3192
3203
  let jsonOk = false;
3193
3204
  try { const j = JSON.parse(r1.stdout); jsonOk = j.feature && j.categories.some(c => c.key === 'auth') && Array.isArray(j.checklist) && j.checklist.length >= 5 && j.network === false; } catch {}
@@ -3202,8 +3213,8 @@ total++;
3202
3213
  }
3203
3214
 
3204
3215
  // 1.9.286 회귀 (UR-0024): skill impact — 영향 경량 상관추적 + 정직한 advisory
3205
- total++;
3206
- {
3216
+ if (e2eShard.take('B(1.9.286)')) {
3217
+ total++;
3207
3218
  const iDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-impact-'));
3208
3219
  cp.spawnSync(process.execPath, [CLI, 'init', iDir, '--minimal', '--no-env', '--yes'], { encoding: 'utf8', timeout: 30000, env: { ...process.env, LEERNESS_OFFLINE: '1' } });
3209
3220
  // 표본 부족 (검증 0건) → 판단 보류 advisory
@@ -3223,8 +3234,8 @@ total++;
3223
3234
  }
3224
3235
 
3225
3236
  // 1.9.287 회귀 (Codex 리뷰 수렴): verify-claim --require-evidence + handoff 펜스 sanitize + status minimal
3226
- total++;
3227
- {
3237
+ if (e2eShard.take('B(1.9.287)')) {
3238
+ total++;
3228
3239
  const cDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-codex-'));
3229
3240
  const env = { ...process.env, LEERNESS_OFFLINE: '1' };
3230
3241
  cp.spawnSync(process.execPath, [CLI, 'init', cDir, '--minimal', '--no-env', '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000, env }); // 1.36.55: 로케일 고정 — auto(en 환경)에서 ko 정규식 단언이 깨지던 이식성 결함
@@ -3258,8 +3269,8 @@ total++;
3258
3269
  }
3259
3270
 
3260
3271
  // 1.9.288 회귀 (Codex gpt-5.5 리뷰 수렴): MCP policy enforce + release dry-run + 도구수 정합
3261
- total++;
3262
- {
3272
+ if (e2eShard.take('B(1.9.288)')) {
3273
+ total++;
3263
3274
  const env = { ...process.env, LEERNESS_OFFLINE: '1' };
3264
3275
  // #1 MCP policy: read-only enforce → state_start(write) 차단 + state.json 미생성
3265
3276
  const pDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mcppol-'));
@@ -3295,8 +3306,8 @@ total++;
3295
3306
  }
3296
3307
 
3297
3308
  // 1.9.289 회귀 (Codex #3): _shellQuoteArg — REPL agy/copilot 프롬프트 셸 주입 중립화
3298
- total++;
3299
- {
3309
+ if (e2eShard.take('B(1.9.289)')) {
3310
+ total++;
3300
3311
  let ok = false;
3301
3312
  try {
3302
3313
  const h = require(path.resolve(__dirname, '..', 'bin', 'leerness.js'));
@@ -3312,8 +3323,8 @@ total++;
3312
3323
  }
3313
3324
 
3314
3325
  // 1.9.290 회귀 (Codex #4 UR-0037): require('harness') 가 호스트 프로세스 오염 X (top-level side effect 격리)
3315
- total++;
3316
- {
3326
+ if (e2eShard.take('B(1.9.290)')) {
3327
+ total++;
3317
3328
  let ok = false;
3318
3329
  try {
3319
3330
  // 깨끗한 자식에서: 내 warning listener 등록 → require → 보존 확인 + NODE_OPTIONS 미변경
@@ -3331,8 +3342,8 @@ total++;
3331
3342
  }
3332
3343
 
3333
3344
  // 1.9.291 회귀 (UR-0025 2단계): lib/agent-registry.js 모듈 분리 — 단일출처 + 부작용 0
3334
- total++;
3335
- {
3345
+ if (e2eShard.take('B(1.9.291)')) {
3346
+ total++;
3336
3347
  let ok = false;
3337
3348
  try {
3338
3349
  const reg = require(path.resolve(__dirname, '..', 'lib', 'agent-registry.js'));
@@ -3352,8 +3363,8 @@ total++;
3352
3363
  }
3353
3364
 
3354
3365
  // 1.9.292 회귀 (UR-0031): leerness context — get_project_context 집약 시맨틱 verb
3355
- total++;
3356
- {
3366
+ if (e2eShard.take('B(1.9.292)')) {
3367
+ total++;
3357
3368
  let ok = false;
3358
3369
  try {
3359
3370
  const r = cp.spawnSync(process.execPath, [CLI, 'context', tmp, '--json'], { cwd: tmp, encoding: 'utf8', timeout: 30000 });
@@ -3370,8 +3381,8 @@ total++;
3370
3381
  }
3371
3382
 
3372
3383
  // 1.9.293 회귀: idempotency audit --auto-fix + progressHeader 복제버그 fix
3373
- total++;
3374
- {
3384
+ if (e2eShard.take('B(1.9.293)')) {
3385
+ total++;
3375
3386
  let ok = false;
3376
3387
  try {
3377
3388
  const idemDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-idem-'));
@@ -3397,8 +3408,8 @@ total++;
3397
3408
  }
3398
3409
 
3399
3410
  // 1.9.294 회귀 (UR-0025 3단계): lib/role-catalog.js 모듈 분리 — 단일출처 + 부작용 0
3400
- total++;
3401
- {
3411
+ if (e2eShard.take('B(1.9.294)')) {
3412
+ total++;
3402
3413
  let ok = false;
3403
3414
  try {
3404
3415
  const reg = require(path.resolve(__dirname, '..', 'lib', 'role-catalog.js'));
@@ -3418,8 +3429,8 @@ total++;
3418
3429
  }
3419
3430
 
3420
3431
  // 1.9.295 회귀 (UR-0025 4단계): lib/catalogs.js 모듈 분리 — 단일출처 + 부작용 0 + 소비 명령 회귀
3421
- total++;
3422
- {
3432
+ if (e2eShard.take('B(1.9.295)')) {
3433
+ total++;
3423
3434
  let ok = false;
3424
3435
  try {
3425
3436
  const reg = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -3443,8 +3454,8 @@ total++;
3443
3454
  }
3444
3455
 
3445
3456
  // 1.9.296 회귀 (UR-0030): leerness about — 정체성(AI 운영 레이어) verb + MCP + README 섹션
3446
- total++;
3447
- {
3457
+ if (e2eShard.take('B(1.9.296)')) {
3458
+ total++;
3448
3459
  let ok = false;
3449
3460
  try {
3450
3461
  const r = cp.spawnSync(process.execPath, [CLI, 'about', '--json'], { cwd: tmp, encoding: 'utf8', timeout: 20000 });
@@ -3467,8 +3478,8 @@ total++;
3467
3478
  }
3468
3479
 
3469
3480
  // 1.9.297 회귀 (UR-0025 5단계): lib/mcp-tools.js 단일출처 — tools/list == 모듈 == _mcpToolCount (Codex #5 영구해소)
3470
- total++;
3471
- {
3481
+ if (e2eShard.take('B(1.9.297)')) {
3482
+ total++;
3472
3483
  let ok = false;
3473
3484
  try {
3474
3485
  const T = require(path.resolve(__dirname, '..', 'lib', 'mcp-tools.js'));
@@ -3487,8 +3498,8 @@ total++;
3487
3498
  }
3488
3499
 
3489
3500
  // 1.9.298 회귀 (UR-0038 외부리뷰): writeUtf8 원자적 쓰기 — 반복 쓰기 후 무손상 + temp 잔여 0
3490
- total++;
3491
- {
3501
+ if (e2eShard.take('B(1.9.298)')) {
3502
+ total++;
3492
3503
  let ok = false;
3493
3504
  try {
3494
3505
  const aDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-atomic-'));
@@ -3518,8 +3529,8 @@ total++;
3518
3529
  }
3519
3530
 
3520
3531
  // 1.9.299 회귀 (UR-0039 외부리뷰): npm test 시크릿 차단 — 토큰이 보이면 exit 1 인 test 스크립트가 verify-code 에서 통과해야(스크럽됨)
3521
- total++;
3522
- {
3532
+ if (e2eShard.take('B(1.9.299)')) {
3533
+ total++;
3523
3534
  let ok = false;
3524
3535
  try {
3525
3536
  const secDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sec-'));
@@ -3544,8 +3555,8 @@ total++;
3544
3555
  }
3545
3556
 
3546
3557
  // 1.9.300 회귀 (UR-0040 외부리뷰): 셸 주입 표면 제거 — fetchNpmLatest execFile + argList 인용 + update --check 회귀
3547
- total++;
3548
- {
3558
+ if (e2eShard.take('B(1.9.300)')) {
3559
+ total++;
3549
3560
  let ok = false;
3550
3561
  try {
3551
3562
  const harnessSrc = fs.readFileSync(path.resolve(__dirname, '..', 'bin', 'leerness.js'), 'utf8');
@@ -3565,8 +3576,8 @@ total++;
3565
3576
  }
3566
3577
 
3567
3578
  // 1.9.301 회귀 (UR-0041 외부리뷰): MCP 정책 게이트가 도구 requiredTier 메타데이터로 under-classify 갭 차단
3568
- total++;
3569
- {
3579
+ if (e2eShard.take('B(1.9.301)')) {
3580
+ total++;
3570
3581
  let ok = false;
3571
3582
  try {
3572
3583
  const pDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tier-'));
@@ -3598,8 +3609,8 @@ total++;
3598
3609
  }
3599
3610
 
3600
3611
  // 1.9.302 회귀 (UR-0042 외부리뷰): verify-claim git diff 시맨틱 교차검증 (변경 파일 주장 ✓ / 미변경 주장 ⚠ + strict FAIL)
3601
- total++;
3602
- {
3612
+ if (e2eShard.take('B(1.9.302)')) {
3613
+ total++;
3603
3614
  let ok = false;
3604
3615
  try {
3605
3616
  const vDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vc-'));
@@ -3626,8 +3637,8 @@ total++;
3626
3637
  }
3627
3638
 
3628
3639
  // 1.9.303 회귀 (UR-0043 외부리뷰): 동시 task add lost-update 락 — 6개 병렬 추가 시 모두 보존 + ID 충돌 0
3629
- total++;
3630
- {
3640
+ if (e2eShard.take('B(1.9.303)')) {
3641
+ total++;
3631
3642
  let ok = false;
3632
3643
  try {
3633
3644
  const lDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lock-'));
@@ -3663,8 +3674,8 @@ total++;
3663
3674
  }
3664
3675
 
3665
3676
  // 1.9.304 회귀 (UR-0025): lib/analyzers.js 모듈 분리 — 단일출처 + 부작용 0 + 소비 명령 회귀
3666
- total++;
3667
- {
3677
+ if (e2eShard.take('B(1.9.304)')) {
3678
+ total++;
3668
3679
  let ok = false;
3669
3680
  try {
3670
3681
  const a = require(path.resolve(__dirname, '..', 'lib', 'analyzers.js'));
@@ -3685,8 +3696,8 @@ total++;
3685
3696
  }
3686
3697
 
3687
3698
  // 1.9.305 회귀 (사용자 명시): honesty-check — AI 인식론적 정직성 3차원 + exit code + MCP 노출
3688
- total++;
3689
- {
3699
+ if (e2eShard.take('B(1.9.305)')) {
3700
+ total++;
3690
3701
  let ok = false;
3691
3702
  try {
3692
3703
  // 양호(근거 있음) → exit 0 + ✓
@@ -3708,8 +3719,8 @@ total++;
3708
3719
  }
3709
3720
 
3710
3721
  // 1.9.306 회귀 (UR-0045 설치리뷰): exit code 일관성 — 오류 경로 exit 1, 정상/help exit 0
3711
- total++;
3712
- {
3722
+ if (e2eShard.take('B(1.9.306)')) {
3723
+ total++;
3713
3724
  let ok = false;
3714
3725
  try {
3715
3726
  const eDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-exit-'));
@@ -3732,8 +3743,8 @@ total++;
3732
3743
  }
3733
3744
 
3734
3745
  // 1.9.307 회귀 (UR-0055 사용자명시): brief — 프로젝트 청사진 set/show/export + README 섹션 + 멱등 업데이트
3735
- total++;
3736
- {
3746
+ if (e2eShard.take('B(1.9.307)')) {
3747
+ total++;
3737
3748
  let ok = false;
3738
3749
  try {
3739
3750
  const bDir = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-brief-'));
@@ -3761,8 +3772,8 @@ total++;
3761
3772
  }
3762
3773
 
3763
3774
  // 1.9.308 회귀 (UR-0055 2단계): brief update --direction 이력 + context 통합 + MCP leerness_brief
3764
- total++;
3765
- {
3775
+ if (e2eShard.take('B(1.9.308)')) {
3776
+ total++;
3766
3777
  let ok = false;
3767
3778
  try {
3768
3779
  const b2 = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-brief2-'));
@@ -3793,8 +3804,8 @@ total++;
3793
3804
  }
3794
3805
 
3795
3806
  // 1.9.309 회귀 (UR-0048 설치리뷰 critical): verify-claim done 주장 evidence 기본강제 + --lenient + MCP 도달
3796
- total++;
3797
- {
3807
+ if (e2eShard.take('B(1.9.309)')) {
3808
+ total++;
3798
3809
  let ok = false;
3799
3810
  try {
3800
3811
  const vc = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vc48-'));
@@ -3819,8 +3830,8 @@ total++;
3819
3830
  }
3820
3831
 
3821
3832
  // 1.9.310 회귀 (UR-0046 설치리뷰): 입력 스키마 검증 — 무효 status/trigger 거부, 유효/--force 통과, every-round 보존
3822
- total++;
3823
- {
3833
+ if (e2eShard.take('B(1.9.310)')) {
3834
+ total++;
3824
3835
  let ok = false;
3825
3836
  try {
3826
3837
  const vd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-val-'));
@@ -3840,8 +3851,8 @@ total++;
3840
3851
  }
3841
3852
 
3842
3853
  // 1.9.311 회귀 (UR-0047 설치리뷰): init 가드 — 미초기화 디렉토리 write 차단, .leerness 미생성, --force 우회, init 후 정상
3843
- total++;
3844
- {
3854
+ if (e2eShard.take('B(1.9.311)')) {
3855
+ total++;
3845
3856
  let ok = false;
3846
3857
  try {
3847
3858
  // dir1: 미초기화 7개 write 차단(exit 1) + .leerness 미생성 + --force 우회
@@ -3879,8 +3890,8 @@ total++;
3879
3890
 
3880
3891
  // 1.9.312 회귀 (UR-0050 설치리뷰): secret 스캐너 현대 키 — sk-proj-/sk-ant-api03-(_)/gho_/Stripe/npm 검출 + exit 1
3881
3892
  // 주의: e2e.js 는 scan 대상이라 키는 문자열 연결로 구성(리터럴 금지 — 자기 repo scan 오탐 방지)
3882
- total++;
3883
- {
3893
+ if (e2eShard.take('B(1.9.312)')) {
3894
+ total++;
3884
3895
  let ok = false;
3885
3896
  try {
3886
3897
  const sd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-secmod-'));
@@ -3908,8 +3919,8 @@ total++;
3908
3919
  }
3909
3920
 
3910
3921
  // 1.9.313 회귀 (UR-0049 설치리뷰): MCP notification 준수 — id 없는 요청 무응답 + ping {} + notifications/* 가드
3911
- total++;
3912
- {
3922
+ if (e2eShard.take('B(1.9.313)')) {
3923
+ total++;
3913
3924
  let ok = false;
3914
3925
  try {
3915
3926
  const input = [
@@ -3934,8 +3945,8 @@ total++;
3934
3945
 
3935
3946
  // 1.9.314 회귀 (UR-0049→UR-0052 설치리뷰): PowerShell 감지 — pwsh7(channel) ps5-chain 오탐 제거 + ps5.1 정상 발화
3936
3947
  // env 마커로 셸/버전 판별(cross-platform) — POWERSHELL_DISTRIBUTION_CHANNEL=pwsh7, Documents\WindowsPowerShell=ps5.1
3937
- total++;
3938
- {
3948
+ if (e2eShard.take('B(1.9.314)')) {
3949
+ total++;
3939
3950
  let ok = false;
3940
3951
  try {
3941
3952
  const sg = (extra) => { const r = cp.spawnSync(process.execPath, [CLI, 'shell-guard', 'a && b', '--json'], { encoding: 'utf8', timeout: 15000, env: { ...process.env, ...extra } }); try { return JSON.parse(r.stdout); } catch { return null; } };
@@ -3950,8 +3961,8 @@ total++;
3950
3961
  }
3951
3962
 
3952
3963
  // 1.9.315 회귀 (UR-0054 설치리뷰): doc/surface 정합 — doctor 진단 명령 + stale MCP 카운트 동적화(commands/banner)
3953
- total++;
3954
- {
3964
+ if (e2eShard.take('B(1.9.315)')) {
3965
+ total++;
3955
3966
  let ok = false; const dbg = {};
3956
3967
  try {
3957
3968
  // 1.36.100: 이 호출만 30초로 남아 있었다 — doctor 는 selftest 를 내장해 격리 실측 31s(게시된 1.36.98 도 동일)라
@@ -3981,8 +3992,8 @@ total++;
3981
3992
 
3982
3993
  // 1.9.316 회귀 (drift 마커 버그): session-handoff 'Last generated' 중복 누적 방지 + drift 'session close 누락' 클리어
3983
3994
  // 근본: sessionClose 프론트매터 추출이 본문 '---' 를 오인 → 구 블록 보존 → 첫(구) Last generated 를 drift 가 읽어 영구 오발화
3984
- total++;
3985
- {
3995
+ if (e2eShard.take('B(1.9.316/1.36.262)')) {
3996
+ total++;
3986
3997
  let ok = false;
3987
3998
  try {
3988
3999
  const dd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-driftmark-'));
@@ -4013,8 +4024,8 @@ total++;
4013
4024
  }
4014
4025
 
4015
4026
  // 1.9.317 회귀 (UR-0051 설치리뷰): 텔레메트리 분리 — 내부 auto-call(LEERNESS_INTERNAL)이 usage 집계 오염 안 함
4016
- total++;
4017
- {
4027
+ if (e2eShard.take('B(1.9.317)')) {
4028
+ total++;
4018
4029
  let ok = false;
4019
4030
  try {
4020
4031
  const td = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-telem-'));
@@ -4039,8 +4050,8 @@ total++;
4039
4050
  }
4040
4051
 
4041
4052
  // 1.9.318 회귀 (UR-0025 모듈화): HTML 파싱 유틸 3종 lib/pure-utils 분리 + harness 인라인 제거 + 동작 보존 + 소비명령 로드
4042
- total++;
4043
- {
4053
+ if (e2eShard.take('B(1.9.318)')) {
4054
+ total++;
4044
4055
  let ok = false;
4045
4056
  try {
4046
4057
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4059,8 +4070,8 @@ total++;
4059
4070
  }
4060
4071
 
4061
4072
  // 1.9.319 회귀 (UR-0044): MCP ToolRegistry 일치성 — tools/list 수 = def 수 + 대표 도구 dispatch(Unknown tool 아님)
4062
- total++;
4063
- {
4073
+ if (e2eShard.take('B(1.9.319)')) {
4074
+ total++;
4064
4075
  let ok = false;
4065
4076
  try {
4066
4077
  const toolsLen = require(path.resolve(__dirname, '..', 'lib', 'mcp-tools.js')).length;
@@ -4076,8 +4087,8 @@ total++;
4076
4087
  }
4077
4088
 
4078
4089
  // 1.9.320 회귀 (UR-0053): count drift — decisions/lessons 카운터가 코드펜스(```md 템플릿 예시) 제외 (decisions=2 실제1 버그)
4079
- total++;
4080
- {
4090
+ if (e2eShard.take('B(1.9.320)')) {
4091
+ total++;
4081
4092
  let ok = false;
4082
4093
  try {
4083
4094
  const cd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-countdrift-'));
@@ -4094,8 +4105,8 @@ total++;
4094
4105
  }
4095
4106
 
4096
4107
  // 1.9.321 회귀 (UR-0053): decision 빈 필드(alternatives)가 [ \t]* 파싱으로 다음 줄(impact)을 캡처하지 않음
4097
- total++;
4098
- {
4108
+ if (e2eShard.take('B(1.9.321)')) {
4109
+ total++;
4099
4110
  let ok = false;
4100
4111
  try {
4101
4112
  const fd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-field-'));
@@ -4113,8 +4124,8 @@ total++;
4113
4124
  }
4114
4125
 
4115
4126
  // 1.9.322 회귀 (UR-0044): _mcpToCliArgs 추출 후 인자 매핑(push) 보존 + 미지 도구 -32601
4116
- total++;
4117
- {
4127
+ if (e2eShard.take('B(1.9.322)')) {
4128
+ total++;
4118
4129
  let ok = false;
4119
4130
  try {
4120
4131
  const md = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tocli-'));
@@ -4135,8 +4146,8 @@ total++;
4135
4146
  }
4136
4147
 
4137
4148
  // 1.9.323 회귀 (UR-0054 ⑥): fresh init → gate(lazy detect) 통과(부재신호 비차단) + active 거짓완료는 차단 유지
4138
- total++;
4139
- {
4149
+ if (e2eShard.take('B(1.9.323)')) {
4150
+ total++;
4140
4151
  let ok = false;
4141
4152
  try {
4142
4153
  const fg = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-freshgate-'));
@@ -4153,8 +4164,8 @@ total++;
4153
4164
  }
4154
4165
 
4155
4166
  // 1.9.324 회귀 (UR-0025 모듈화): 메모리 MD 파서 2종 lib/pure-utils 분리 + harness 인라인 제거 + _compareSemver 중복제거 + 소비명령
4156
- total++;
4157
- {
4167
+ if (e2eShard.take('B(1.9.324)')) {
4168
+ total++;
4158
4169
  let ok = false;
4159
4170
  try {
4160
4171
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4180,8 +4191,8 @@ total++;
4180
4191
  }
4181
4192
 
4182
4193
  // 1.9.325 회귀 (UR-0025 모듈화): _classifyIntent lib/pure-utils 분리 + harness 인라인 제거 + 소비명령(intent classify)
4183
- total++;
4184
- {
4194
+ if (e2eShard.take('B(1.9.325)')) {
4195
+ total++;
4185
4196
  let ok = false;
4186
4197
  try {
4187
4198
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4206,8 +4217,8 @@ total++;
4206
4217
  }
4207
4218
 
4208
4219
  // 1.9.326 회귀 (UR-0025 모듈화): 순수 문자열/셸/env 유틸 3종 lib/pure-utils 분리 + harness 인라인 제거
4209
- total++;
4210
- {
4220
+ if (e2eShard.take('B(1.9.326)')) {
4221
+ total++;
4211
4222
  let ok = false;
4212
4223
  try {
4213
4224
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4230,8 +4241,8 @@ total++;
4230
4241
  }
4231
4242
 
4232
4243
  // 1.9.327 회귀 (UR-0025 모듈화): TZ/날짜 포맷 2종 lib/pure-utils 분리 + harness 인라인 제거
4233
- total++;
4234
- {
4244
+ if (e2eShard.take('B(1.9.327)')) {
4245
+ total++;
4235
4246
  let ok = false;
4236
4247
  try {
4237
4248
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4250,8 +4261,8 @@ total++;
4250
4261
  }
4251
4262
 
4252
4263
  // 1.9.328 회귀 (UR-0025 모듈화): 순수 문자열 유틸 2종(_truncate/_splitList) lib/pure-utils 분리 + harness 인라인 제거
4253
- total++;
4254
- {
4264
+ if (e2eShard.take('B(1.9.328)')) {
4265
+ total++;
4255
4266
  let ok = false;
4256
4267
  try {
4257
4268
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4269,8 +4280,8 @@ total++;
4269
4280
  }
4270
4281
 
4271
4282
  // 1.9.329 회귀 (UR-0025 모듈화): roadmap MD 파서 3종 lib/pure-utils 분리 + harness 인라인 제거
4272
- total++;
4273
- {
4283
+ if (e2eShard.take('B(1.9.329)')) {
4284
+ total++;
4274
4285
  let ok = false;
4275
4286
  try {
4276
4287
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4289,8 +4300,8 @@ total++;
4289
4300
  }
4290
4301
 
4291
4302
  // 1.9.330 회귀 (UR-0025 모듈화): project-brief config(_BRIEF_FIELDS) + _briefFilled lib/pure-utils 분리 + brief 명령 회귀
4292
- total++;
4293
- {
4303
+ if (e2eShard.take('B(1.9.330)')) {
4304
+ total++;
4294
4305
  let ok = false;
4295
4306
  try {
4296
4307
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4314,8 +4325,8 @@ total++;
4314
4325
  }
4315
4326
 
4316
4327
  // 1.9.331 회귀 (UR-0025 서브시스템): brief 빌더(_briefReadmeBlock/_briefBlueprint)+마커 lib/pure-utils 분리 + brief export/sync 회귀
4317
- total++;
4318
- {
4328
+ if (e2eShard.take('B(1.9.331)')) {
4329
+ total++;
4319
4330
  let ok = false;
4320
4331
  try {
4321
4332
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4342,8 +4353,8 @@ total++;
4342
4353
  }
4343
4354
 
4344
4355
  // 1.9.332 회귀 (UR-0025 모듈화): lessons.md 파서(_parseLessonEntries) lib/pure-utils 분리 + lesson list 회귀
4345
- total++;
4346
- {
4356
+ if (e2eShard.take('B(1.9.332)')) {
4357
+ total++;
4347
4358
  let ok = false;
4348
4359
  try {
4349
4360
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4366,8 +4377,8 @@ total++;
4366
4377
  }
4367
4378
 
4368
4379
  // 1.9.333 회귀 (UR-0025 심층): constraints 서브시스템 핵심 분리 — catalog→lib/catalogs + _matchConstraints→pure-utils + constraints 명령 회귀
4369
- total++;
4370
- {
4380
+ if (e2eShard.take('B(1.9.333)')) {
4381
+ total++;
4371
4382
  let ok = false;
4372
4383
  try {
4373
4384
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4402,8 +4413,8 @@ total++;
4402
4413
  }
4403
4414
 
4404
4415
  // 1.9.334 회귀 (UR-0025 심층, Codex 위임·검증): intent domain catalog→lib/catalogs + _matchDomain→pure-utils + intent expand 회귀
4405
- total++;
4406
- {
4416
+ if (e2eShard.take('B(1.9.334)')) {
4417
+ total++;
4407
4418
  let ok = false;
4408
4419
  try {
4409
4420
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4428,8 +4439,8 @@ total++;
4428
4439
  }
4429
4440
 
4430
4441
  // 1.9.335 회귀 (UR-0025 심층): LSP catalog→lib/catalogs + _detectLspLang/_matchLspSymbols→pure-utils + lsp symbols 회귀
4431
- total++;
4432
- {
4442
+ if (e2eShard.take('B(1.9.335)')) {
4443
+ total++;
4433
4444
  let ok = false;
4434
4445
  try {
4435
4446
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4456,8 +4467,8 @@ total++;
4456
4467
  }
4457
4468
 
4458
4469
  // 1.9.336 회귀 (UR-0025 심층, Codex 위임·검증): anti-laziness OPTIMISM_PATTERNS→lib/catalogs + optimism 순수로직→pure-utils + optimism-check 회귀
4459
- total++;
4460
- {
4470
+ if (e2eShard.take('B(1.9.336)')) {
4471
+ total++;
4461
4472
  let ok = false;
4462
4473
  try {
4463
4474
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4489,8 +4500,8 @@ total++;
4489
4500
  }
4490
4501
 
4491
4502
  // 1.9.337 회귀 (UR-0025 심층): persona BUILT_IN_PERSONAS→lib/catalogs + _personaSummaries→pure-utils + persona list/review 회귀
4492
- total++;
4493
- {
4503
+ if (e2eShard.take('B(1.9.337)')) {
4504
+ total++;
4494
4505
  let ok = false;
4495
4506
  try {
4496
4507
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4519,8 +4530,8 @@ total++;
4519
4530
  }
4520
4531
 
4521
4532
  // 1.9.338 회귀 (UR-0025 심층): i18n STRINGS→lib/catalogs + _translate→pure-utils + _t 박막 (인터랙티브 전용 getter라 구조+순수동작 검증)
4522
- total++;
4523
- {
4533
+ if (e2eShard.take('B(1.9.338)')) {
4534
+ total++;
4524
4535
  let ok = false;
4525
4536
  try {
4526
4537
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4544,8 +4555,8 @@ total++;
4544
4555
  }
4545
4556
 
4546
4557
  // 1.9.339 회귀 (UR-0053): decisions canonical = JSON, MD = projection. add→json+md / context count 단일소스 / drop+archive / MD-only 백필
4547
- total++;
4548
- {
4558
+ if (e2eShard.take('B(1.9.339)')) {
4559
+ total++;
4549
4560
  let ok = false;
4550
4561
  try {
4551
4562
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4591,8 +4602,8 @@ total++;
4591
4602
  }
4592
4603
 
4593
4604
  // 1.9.340 회귀 (UR-0058, Codex 위임·검증): lessons canonical = JSON, MD = projection. save→json+md / list+tag / drop+archive / MD-only 백필
4594
- total++;
4595
- {
4605
+ if (e2eShard.take('B(1.9.340)')) {
4606
+ total++;
4596
4607
  let ok = false;
4597
4608
  try {
4598
4609
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4637,8 +4648,8 @@ total++;
4637
4648
  }
4638
4649
 
4639
4650
  // 1.9.341 회귀 (UR-0025 심층): BUILTIN_CATALOG→lib/catalogs + _withBuiltinSource→pure-utils + skill list 회귀
4640
- total++;
4641
- {
4651
+ if (e2eShard.take('B(1.9.341)')) {
4652
+ total++;
4642
4653
  let ok = false;
4643
4654
  try {
4644
4655
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4667,8 +4678,8 @@ total++;
4667
4678
  }
4668
4679
 
4669
4680
  // 1.9.342 회귀 (UR-0025 심층): ROADMAP_STATUS_LABEL/COLOR→lib/catalogs + roadmap.html 렌더 회귀
4670
- total++;
4671
- {
4681
+ if (e2eShard.take('B(1.9.342)')) {
4682
+ total++;
4672
4683
  let ok = false;
4673
4684
  try {
4674
4685
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4694,8 +4705,8 @@ total++;
4694
4705
  }
4695
4706
 
4696
4707
  // 1.9.343 회귀 (UR-0025 심층, R300 마일스톤): SECRET_PATTERNS→lib/catalogs 보안 응집 + scan secrets 탐지/오탐 가드 회귀
4697
- total++;
4698
- {
4708
+ if (e2eShard.take('B(1.9.343)')) {
4709
+ total++;
4699
4710
  let ok = false;
4700
4711
  try {
4701
4712
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4729,8 +4740,8 @@ total++;
4729
4740
  }
4730
4741
 
4731
4742
  // 1.9.344 회귀 (UR-0025 심층): SKILL_CATALOG_PRESETS→lib/catalogs + skill discover preset 인식 회귀
4732
- total++;
4733
- {
4743
+ if (e2eShard.take('B(1.9.344)')) {
4744
+ total++;
4734
4745
  let ok = false;
4735
4746
  try {
4736
4747
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
@@ -4754,8 +4765,8 @@ total++;
4754
4765
  }
4755
4766
 
4756
4767
  // 1.9.345 회귀 (UR-0025 심층): _esc(HTML escape)→pure-utils + roadmap.html XSS 이스케이프 회귀
4757
- total++;
4758
- {
4768
+ if (e2eShard.take('B(1.9.345)')) {
4769
+ total++;
4759
4770
  let ok = false;
4760
4771
  try {
4761
4772
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4782,8 +4793,8 @@ total++;
4782
4793
  }
4783
4794
 
4784
4795
  // 1.9.346 회귀 (UR-0025 심층): _roadmapTokenStyles→pure-utils + roadmap.html CSS 변수 회귀
4785
- total++;
4786
- {
4796
+ if (e2eShard.take('B(1.9.346)')) {
4797
+ total++;
4787
4798
  let ok = false;
4788
4799
  try {
4789
4800
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4809,8 +4820,8 @@ total++;
4809
4820
  }
4810
4821
 
4811
4822
  // 1.9.347 회귀 (UR-0025 심층): _parseSkillMd(SKILL.md frontmatter, BOM-aware)→pure-utils + skill install(BOM) 회귀
4812
- total++;
4813
- {
4823
+ if (e2eShard.take('B(1.9.347)')) {
4824
+ total++;
4814
4825
  let ok = false;
4815
4826
  try {
4816
4827
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4836,8 +4847,8 @@ total++;
4836
4847
  }
4837
4848
 
4838
4849
  // 1.9.348 회귀 (외부리뷰 UR-0059 P0): --path 라우팅 일관화 — positional-dispatch 가 --path 우선(→positional→cwd)
4839
- total++;
4840
- {
4850
+ if (e2eShard.take('B(1.9.348)')) {
4851
+ total++;
4841
4852
  let ok = false;
4842
4853
  try {
4843
4854
  const A = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-pa-'));
@@ -4862,8 +4873,8 @@ total++;
4862
4873
  }
4863
4874
 
4864
4875
  // 1.9.349 회귀 (외부리뷰 UR-0063, GPT5.5+Opus 교차검증): selftest/doctor 위치독립 — 비초기화 dir 에서도 통과(거짓 "설치 손상" 없음)
4865
- total++;
4866
- {
4876
+ if (e2eShard.take('B(1.9.349)')) {
4877
+ total++;
4867
4878
  let ok = false; let _liDbg = null;
4868
4879
  try {
4869
4880
  const ni = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-noinit-')); // .leerness 없는 비초기화 dir
@@ -4892,8 +4903,8 @@ total++;
4892
4903
  }
4893
4904
 
4894
4905
  // 1.9.350 회귀 (외부리뷰 P1 보안 하드닝): UR-0061 CSS breakout 차단 + UR-0062 skill traversal 차단 + UR-0060 scan false-neg/패턴 보강
4895
- total++;
4896
- {
4906
+ if (e2eShard.take('B(1.9.350)')) {
4907
+ total++;
4897
4908
  let ok = false;
4898
4909
  try {
4899
4910
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4922,8 +4933,8 @@ total++;
4922
4933
  }
4923
4934
 
4924
4935
  // 1.9.351 회귀 (외부리뷰 UR-0064/0065): decision/lesson 제목 오염 차단 + 문서 정합(AGENTS.md .leerness / --help 누락 명령)
4925
- total++;
4926
- {
4936
+ if (e2eShard.take('B(1.9.351)')) {
4937
+ total++;
4927
4938
  let ok = false;
4928
4939
  try {
4929
4940
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-doc-'));
@@ -4947,8 +4958,8 @@ total++;
4947
4958
  }
4948
4959
 
4949
4960
  // 1.9.352 회귀 (외부리뷰 P2): UR-0069 usage subcommand 집계 + UR-0068 milestone 파서 블록 경계
4950
- total++;
4951
- {
4961
+ if (e2eShard.take('B(1.9.352)')) {
4962
+ total++;
4952
4963
  let ok = false;
4953
4964
  try {
4954
4965
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -4972,8 +4983,8 @@ total++;
4972
4983
  }
4973
4984
 
4974
4985
  // 1.9.353 회귀 (외부리뷰 P2): UR-0067 encoding(CP949 감지/.ps1 BOM 예외) + UR-0070 shell-guard --shell + UR-0071 init non-TTY ANSI
4975
- total++;
4976
- {
4986
+ if (e2eShard.take('B(1.9.353)')) {
4987
+ total++;
4977
4988
  let ok = false;
4978
4989
  try {
4979
4990
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-enc-'));
@@ -5002,8 +5013,8 @@ total++;
5002
5013
  }
5003
5014
 
5004
5015
  // 1.9.354 회귀 (외부리뷰 P3 UR-0072): compareVer pre-release + _classifyCJK + scan secrets 파일경로 + requests drop ✓
5005
- total++;
5006
- {
5016
+ if (e2eShard.take('B(1.9.354)')) {
5017
+ total++;
5007
5018
  let ok = false;
5008
5019
  try {
5009
5020
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -5030,8 +5041,8 @@ total++;
5030
5041
  }
5031
5042
 
5032
5043
  // 1.9.355 회귀 (UR-0075 Phase A): migrate --guide 가이드 출력 + update/init/migrate --path 타깃(이전 positional 전용)
5033
- total++;
5034
- {
5044
+ if (e2eShard.take('B(1.9.355)')) {
5045
+ total++;
5035
5046
  let ok = false;
5036
5047
  try {
5037
5048
  const m = require(path.resolve(__dirname, '..', 'lib', 'pure-utils.js'));
@@ -5056,8 +5067,8 @@ total++;
5056
5067
  }
5057
5068
 
5058
5069
  // 1.9.356 회귀 (UR-0075 Phase B): migrate audit — clean=변경없음 · 구버전+canonical 누락=findings (비파괴 dry-run)
5059
- total++;
5060
- {
5070
+ if (e2eShard.take('B(1.9.356)')) {
5071
+ total++;
5061
5072
  let ok = false;
5062
5073
  try {
5063
5074
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-aud-'));
@@ -5083,8 +5094,8 @@ total++;
5083
5094
  }
5084
5095
 
5085
5096
  // 1.9.357 회귀 (UR-0075 Phase C): migrate apply — dry-run 비파괴 · --yes canonical 백필 · 멱등
5086
- total++;
5087
- {
5097
+ if (e2eShard.take('B(1.9.357/1.36.191)')) {
5098
+ total++;
5088
5099
  let ok = false;
5089
5100
  try {
5090
5101
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-app-'));
@@ -5123,8 +5134,8 @@ total++;
5123
5134
  }
5124
5135
 
5125
5136
  // 1.9.358 회귀 (UR-0075 Phase D): migrate plan — 임시폴더 설치 후 비교 · clean=변경없음 · 코어파일 누락=missing 감지 · 읽기전용
5126
- total++;
5127
- {
5137
+ if (e2eShard.take('B(1.9.358)')) {
5138
+ total++;
5128
5139
  let ok = false;
5129
5140
  try {
5130
5141
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-plan-'));
@@ -5148,8 +5159,8 @@ total++;
5148
5159
  }
5149
5160
 
5150
5161
  // 1.9.359 회귀 (UR-0074): install-safety — 0 런타임 deps / 0 install-script / safe-install 워크플로 (공급망 신뢰 가드)
5151
- total++;
5152
- {
5162
+ if (e2eShard.take('B(1.9.359)')) {
5163
+ total++;
5153
5164
  let ok = false;
5154
5165
  try {
5155
5166
  const r = cp.spawnSync(process.execPath, [CLI, 'install-safety', '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -5161,8 +5172,8 @@ total++;
5161
5172
  }
5162
5173
 
5163
5174
  // 1.9.360 회귀 (외부리뷰 CV-2/UR-0077): update --check 가 신형 Node Windows 에서 spawn EINVAL 없이 동작 (cmd.exe 경유)
5164
- total++;
5165
- {
5175
+ if (e2eShard.take('B(1.9.360)')) {
5176
+ total++;
5166
5177
  let ok = false;
5167
5178
  try {
5168
5179
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-einval-'));
@@ -5178,8 +5189,8 @@ total++;
5178
5189
  }
5179
5190
 
5180
5191
  // 1.9.361 회귀 (외부리뷰 CV-1/UR-0076): --path 라우팅 통일 — session close --path 가 cwd 아닌 정타깃에 쓰기 + context --path= 등호형
5181
- total++;
5182
- {
5192
+ if (e2eShard.take('B(1.9.361)')) {
5193
+ total++;
5183
5194
  let ok = false;
5184
5195
  try {
5185
5196
  const a = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rootA-'));
@@ -5205,8 +5216,8 @@ total++;
5205
5216
  }
5206
5217
 
5207
5218
  // 1.9.361 회귀 (외부리뷰 CV-3/UR-0078): audit 가 미초기화/존재하지않는 경로를 healthy 로 오판 안 함 (verify 와 일관)
5208
- total++;
5209
- {
5219
+ if (e2eShard.take('B(1.9.361) #2')) {
5220
+ total++;
5210
5221
  let ok = false;
5211
5222
  try {
5212
5223
  const r = cp.spawnSync(process.execPath, [CLI, 'audit', path.join(os.tmpdir(), 'leerness-no-such-' + total), '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -5218,8 +5229,8 @@ total++;
5218
5229
  }
5219
5230
 
5220
5231
  // 1.9.362 회귀 (외부리뷰 CV-4/UR-0079): archive retention — init/migrate 반복해도 --keep 상한 유지 (무한 누적 차단)
5221
- total++;
5222
- {
5232
+ if (e2eShard.take('B(1.9.362)')) {
5233
+ total++;
5223
5234
  let ok = false;
5224
5235
  try {
5225
5236
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-arch-'));
@@ -5239,8 +5250,8 @@ total++;
5239
5250
  }
5240
5251
 
5241
5252
  // 1.9.363 회귀 (외부리뷰 CV-7/UR-0082): commands 카탈로그가 누락 명령군(8그룹+install-safety+migrate sub) 전수 등재
5242
- total++;
5243
- {
5253
+ if (e2eShard.take('B(1.9.363)')) {
5254
+ total++;
5244
5255
  let ok = false;
5245
5256
  try {
5246
5257
  const r = cp.spawnSync(process.execPath, [CLI, 'commands', '--json'], { encoding: 'utf8', timeout: 15000 });
@@ -5254,8 +5265,8 @@ total++;
5254
5265
  }
5255
5266
 
5256
5267
  // 1.9.364 회귀 (4번째 외부평가 9.3/UR-0083): auto-update hook 비침투 — update --check --quiet 가 up-to-date 시 무음 + hook 명령이 --quiet 사용
5257
- total++;
5258
- {
5268
+ if (e2eShard.take('B(1.9.364)')) {
5269
+ total++;
5259
5270
  let ok = false;
5260
5271
  try {
5261
5272
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-quiet-'));
@@ -5274,8 +5285,8 @@ total++;
5274
5285
  }
5275
5286
 
5276
5287
  // 1.9.365 회귀 (외부리뷰 CV-6/UR-0081): 시크릿 스캐너 정밀도 — placeholder FP 무시 / unquoted FN 탐지 / gitignored 강등
5277
- total++;
5278
- {
5288
+ if (e2eShard.take('B(1.9.365)')) {
5289
+ total++;
5279
5290
  let ok = false;
5280
5291
  try {
5281
5292
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sec-'));
@@ -5301,8 +5312,8 @@ total++;
5301
5312
  }
5302
5313
 
5303
5314
  // 1.9.366 회귀 (외부리뷰 CV-5/UR-0080): selftest 무결성 — 설치 패키지 관점에서 --json 전부 통과 (행위 케이스 포함)
5304
- total++;
5305
- {
5315
+ if (e2eShard.take('B(1.9.366)')) {
5316
+ total++;
5306
5317
  let ok = false;
5307
5318
  try {
5308
5319
  // 1.36.85: 행위검사 전환으로 selftest 약 13s — 4초 시절 기준 30s 는 전체 부하에서 터진다.
@@ -5319,8 +5330,8 @@ total++;
5319
5330
  // 또한 stderr 잡음 0 을 함께 단언한다 — 잡음의 실제 원인(프로브가 fail() 의 stderr 를 안 가로챔 · graph 케이스 quiet 누락)은
5320
5331
  // io 래치가 사람용 log 를 stderr 로 강등해 주는 **우연** 덕에 stdout 만 겨우 지켜졌다. 래치가 바뀌면 곧장 stdout 오염이 된다.
5321
5332
  // → stdout 순수성만 단언하면 "우연히 통과"하므로, 잡음 자체를 0 으로 고정해 원인 단계에서 막는다.
5322
- total++;
5323
- {
5333
+ if (e2eShard.take('B(1.36.88)')) {
5334
+ total++;
5324
5335
  let ok = false, why = '';
5325
5336
  // T-0341: run in a harness made through the product (scripts/e2e-lived-in-harness.js), the same on every tree.
5326
5337
  // T-0315 copied this repository's own .leerness, AGENTS.md and CLAUDE.md, and the public export has none of
@@ -5361,8 +5372,8 @@ total++;
5361
5372
  // 프로젝트 상태에 따라 달라져 같은 코드로 게이트가 통과/실패로 갈렸다. 한 곳에서 나온 결함은 대개 클래스다 —
5362
5373
  // 실측 스윕(조회성 79개)에서 정보성 누출은 audit 하나였고 나머지 4건은 **사용법 오류의 stderr 진단**(CLI 관례)이었다.
5363
5374
  // 그 측정을 래칫으로 고정한다: 성공 경로의 `--json` 은 stderr 가 비어야 한다.
5364
- total++;
5365
- {
5375
+ if (e2eShard.take('B(1.36.121)')) {
5376
+ total++;
5366
5377
  let ok = false; const bad = []; const diagnostics = {};
5367
5378
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-json121-'));
5368
5379
  const ENV = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -5408,8 +5419,8 @@ total++;
5408
5419
  // 실측으로 두 결함이 나왔다: ① `leerness_selftest` 는 실측 116초인데 MCP 기본 한도가 60초라 **항상 타임아웃**
5409
5420
  // (광고된 89개 도구 중 설치 검증 도구가 MCP 로는 무용) ② selftest 프로브가 `process.env` 를 상속해
5410
5421
  // MCP 래퍼가 끄는 안내를 단언 → MCP 경유에서만 1건 실패 = AI 에게 "설치가 깨졌다" 는 **거짓 보고**.
5411
- total++;
5412
- {
5422
+ if (e2eShard.take('B(1.36.124)')) {
5423
+ total++;
5413
5424
  let ok = false, mcpSeconds = null; const bad = [];
5414
5425
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mcp124-'));
5415
5426
  const ENVm = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -5503,8 +5514,8 @@ total++;
5503
5514
  }
5504
5515
 
5505
5516
  // 1.9.367 회귀 (UR-0025): _mergeEnvLines 모듈 분리 — migrate 가 사용자 .env 값을 key-aware 로 보존 (덮어쓰기 X)
5506
- total++;
5507
- {
5517
+ if (e2eShard.take('B(1.9.367)')) {
5518
+ total++;
5508
5519
  let ok = false;
5509
5520
  try {
5510
5521
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-envm-'));
@@ -5521,8 +5532,8 @@ total++;
5521
5532
  }
5522
5533
 
5523
5534
  // 1.9.368 회귀 (UR-0025): _managedMerge 모듈 분리 — migrate 가 사용자 편집(AGENTS.md)을 migration-preserved 블록으로 보존
5524
- total++;
5525
- {
5535
+ if (e2eShard.take('B(1.9.368)')) {
5536
+ total++;
5526
5537
  let ok = false;
5527
5538
  try {
5528
5539
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mm-'));
@@ -5542,8 +5553,8 @@ total++;
5542
5553
  // `adapter` 처럼 새 백업을 만들지 않는 실행이 일반 경로(`.leerness/archive`)를 채워 넘겨서,
5543
5554
  // 이전 마이그레이션이 적어 둔 구체 경로(`.leerness/archive/leerness-1.36.100-…`)를 덮었다.
5544
5555
  // 그 문장은 "삭제하지 않았다" 는 약속의 근거이므로 격하는 곧 정보 손실이다. 세 방향을 모두 잰다.
5545
- total++;
5546
- {
5556
+ if (e2eShard.take('B(1.36.122)')) {
5557
+ total++;
5547
5558
  let ok = false; const bad = [];
5548
5559
  try {
5549
5560
  const PUx = require(path.resolve(path.dirname(CLI), '..', 'lib', 'pure-utils'));
@@ -5600,8 +5611,8 @@ total++;
5600
5611
  }
5601
5612
 
5602
5613
  // 1.9.369 회귀 (UR-0025): MINIMAL_SKIP_KEYS/_parseSkillsValue 분리 — init --minimal 비핵심 스킵+코어 유지, --skills recommended 설치
5603
- total++;
5604
- {
5614
+ if (e2eShard.take('B(1.9.369)')) {
5615
+ total++;
5605
5616
  let ok = false;
5606
5617
  try {
5607
5618
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-min-'));
@@ -5620,8 +5631,8 @@ total++;
5620
5631
  }
5621
5632
 
5622
5633
  // 1.9.370 회귀 (UR-0025): _parseArchiveBlocks/_parseSkillCatalog 순수 파서 모듈 분리 — lib/pure-utils 직접 호출 행위
5623
- total++;
5624
- {
5634
+ if (e2eShard.take('B(1.9.370)')) {
5635
+ total++;
5625
5636
  let ok = false;
5626
5637
  try {
5627
5638
  const pu = require(path.resolve(__dirname, '..', 'lib', 'pure-utils'));
@@ -5636,8 +5647,8 @@ total++;
5636
5647
  }
5637
5648
 
5638
5649
  // 1.9.371 회귀 (UR-0073 Phase A): team 정의 레지스트리 — add/list/remove + canonical JSON/MD (opt-in · 정의 전용)
5639
- total++;
5640
- {
5650
+ if (e2eShard.take('B(1.9.371)')) {
5651
+ total++;
5641
5652
  let ok = false;
5642
5653
  try {
5643
5654
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-team-'));
@@ -5660,8 +5671,8 @@ total++;
5660
5671
  }
5661
5672
 
5662
5673
  // 1.9.372 회귀 (UR-0073 Phase B): team preview — dry-run 실행 계획 미리보기 (멤버별 dispatch 명령, 실제 실행/파일변경 없음)
5663
- total++;
5664
- {
5674
+ if (e2eShard.take('B(1.9.372)')) {
5675
+ total++;
5665
5676
  let ok = false;
5666
5677
  try {
5667
5678
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tprev-'));
@@ -5683,8 +5694,8 @@ total++;
5683
5694
  }
5684
5695
 
5685
5696
  // 1.9.373 회귀 (UR-0073 Phase C): handoff 가 비-manual 팀 스케줄 알림 노출(미리보기 안내) · manual 미표시 · opt-out
5686
- total++;
5687
- {
5697
+ if (e2eShard.take('B(1.9.373)')) {
5698
+ total++;
5688
5699
  let ok = false;
5689
5700
  try {
5690
5701
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tsch-'));
@@ -5704,8 +5715,8 @@ total++;
5704
5715
  }
5705
5716
 
5706
5717
  // 1.9.374 회귀 (UR-0074): release cadence 진단 — --json level/recommendation/releasesPerDay 구조 (릴리스 빈도 가시화)
5707
- total++;
5708
- {
5718
+ if (e2eShard.take('B(1.9.374)')) {
5719
+ total++;
5709
5720
  let ok = false;
5710
5721
  try {
5711
5722
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cad-'));
@@ -5723,8 +5734,8 @@ total++;
5723
5734
  }
5724
5735
 
5725
5736
  // 1.9.376 회귀 (UR-0073 Phase D): team deploy 2중 게이트 — dry-run 실행안함 · --yes만이면 거부 · --yes+env 만 실행
5726
- total++;
5727
- {
5737
+ if (e2eShard.take('B(1.9.376)')) {
5738
+ total++;
5728
5739
  let ok = false;
5729
5740
  try {
5730
5741
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tdep-'));
@@ -5748,8 +5759,8 @@ total++;
5748
5759
  }
5749
5760
 
5750
5761
  // 1.9.377 회귀 (UR-0025): _renderWorkspaceReferenceGuide 모듈 분리 — 빌더가 dirName/version 반영 + 핵심 섹션 포함 (출력 동일성)
5751
- total++;
5752
- {
5762
+ if (e2eShard.take('B(1.9.377)')) {
5763
+ total++;
5753
5764
  let ok = false;
5754
5765
  try {
5755
5766
  const pu = require(path.resolve(__dirname, '..', 'lib', 'pure-utils'));
@@ -5765,8 +5776,8 @@ total++;
5765
5776
  }
5766
5777
 
5767
5778
  // 1.9.378 회귀 (UR-0073): team MCP 도구 2종(read-only) 정의 + 매핑 CLI(team list/preview) 동작
5768
- total++;
5769
- {
5779
+ if (e2eShard.take('B(1.9.378)')) {
5780
+ total++;
5770
5781
  let ok = false;
5771
5782
  try {
5772
5783
  const tools = require(path.resolve(__dirname, '..', 'lib', 'mcp-tools'));
@@ -5788,8 +5799,8 @@ total++;
5788
5799
  }
5789
5800
 
5790
5801
  // 1.9.379 회귀 (UR-0025 심화): pulse 렌더 코어(_memorySurface/_renderPulseLine) 분리 — 빌더 동작 + pulse CLI 출력 유지
5791
- total++;
5792
- {
5802
+ if (e2eShard.take('B(1.9.379)')) {
5803
+ total++;
5793
5804
  let ok = false;
5794
5805
  try {
5795
5806
  const pu = require(path.resolve(__dirname, '..', 'lib', 'pure-utils'));
@@ -5809,8 +5820,8 @@ total++;
5809
5820
  }
5810
5821
 
5811
5822
  // 1.9.380 회귀 (UR-0025): REQUIRED_WORKSPACE_FILES 단일출처 — verify 가 catalog 리스트로 init통과/누락실패
5812
- total++;
5813
- {
5823
+ if (e2eShard.take('B(1.9.380)')) {
5824
+ total++;
5814
5825
  let ok = false;
5815
5826
  try {
5816
5827
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs'));
@@ -5830,8 +5841,8 @@ total++;
5830
5841
  }
5831
5842
 
5832
5843
  // 1.9.381 회귀 (UR-0025): KEYWORD_STOPWORDS 단일출처 — catalog Set + 키워드 필터 동작 + handoff consumer 무크래시
5833
- total++;
5834
- {
5844
+ if (e2eShard.take('B(1.9.381)')) {
5845
+ total++;
5835
5846
  let ok = false;
5836
5847
  try {
5837
5848
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs'));
@@ -5853,8 +5864,8 @@ total++;
5853
5864
  }
5854
5865
 
5855
5866
  // 1.9.382 회귀 (UR-0025 큰핸들러토대): lib/io.js 프리미티브 분리 — exports + 동작 + init(ok)/verify(fail→exit1) consumer 유지
5856
- total++;
5857
- {
5867
+ if (e2eShard.take('B(1.9.382)')) {
5868
+ total++;
5858
5869
  let ok = false;
5859
5870
  try {
5860
5871
  const io = require(path.resolve(__dirname, '..', 'lib', 'io'));
@@ -5873,8 +5884,8 @@ total++;
5873
5884
  }
5874
5885
 
5875
5886
  // 1.9.383 회귀 (UR-0025 큰핸들러토대): lib/io.js fs 프리미티브 분리 — round-trip + decision write→context read consumer 유지
5876
- total++;
5877
- {
5887
+ if (e2eShard.take('B(1.9.383)')) {
5888
+ total++;
5878
5889
  let ok = false;
5879
5890
  try {
5880
5891
  const io = require(path.resolve(__dirname, '..', 'lib', 'io'));
@@ -5897,8 +5908,8 @@ total++;
5897
5908
  }
5898
5909
 
5899
5910
  // 1.9.384 회귀 (5번째 외부평가/UR-0085): status/verify --json 구조화 출력 일관성 — pass JSON + fail JSON(exit1) + 사람용 exit1 유지
5900
- total++;
5901
- {
5911
+ if (e2eShard.take('B(1.9.384)')) {
5912
+ total++;
5902
5913
  let ok = false;
5903
5914
  try {
5904
5915
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-svjson-'));
@@ -5923,8 +5934,8 @@ total++;
5923
5934
  }
5924
5935
 
5925
5936
  // 1.9.385 회귀 (5번째 외부평가/UR-0086): contract verify spec 파서가 markdown bullet "- name(args)" 함수 선언 감지 + backtick 약언급 관대 유지
5926
- total++;
5927
- {
5937
+ if (e2eShard.take('B(1.9.385)')) {
5938
+ total++;
5928
5939
  let ok = false;
5929
5940
  try {
5930
5941
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-contract-bullet-'));
@@ -5952,8 +5963,8 @@ total++;
5952
5963
  }
5953
5964
 
5954
5965
  // 1.9.386 회귀 (5번째 외부평가/UR-0087): secret scan env-family 포함 + gitignore git-일치 (.env↛.env.bad 미보호=커밋대상)
5955
- total++;
5956
- {
5966
+ if (e2eShard.take('B(1.9.386)')) {
5967
+ total++;
5957
5968
  let ok = false;
5958
5969
  try {
5959
5970
  const key = 'OPENAI_API_KEY=sk-proj-abc123def456ghi789jkl012mno345pqr678stuvwx\n';
@@ -5983,8 +5994,8 @@ total++;
5983
5994
  }
5984
5995
 
5985
5996
  // 1.9.387 회귀 (5번째 외부평가 일관성/UR-0088): incident/runs list 빈 케이스도 --json 구조화 {total:0,items:[]} + 사람용 보존
5986
- total++;
5987
- {
5997
+ if (e2eShard.take('B(1.9.387)')) {
5998
+ total++;
5988
5999
  let ok = false;
5989
6000
  try {
5990
6001
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-listjson-'));
@@ -6005,8 +6016,8 @@ total++;
6005
6016
  }
6006
6017
 
6007
6018
  // 1.9.393 회귀 (UR-0025 + whats-new BUG-fix): _parseChangelogBetween 가 "## X — DATE — title" 헤더를 파싱 (종전 0건 반환 버그)
6008
- total++;
6009
- {
6019
+ if (e2eShard.take('B(1.9.393)')) {
6020
+ total++;
6010
6021
  let ok = false;
6011
6022
  try {
6012
6023
  const pkgRoot = path.resolve(__dirname, '..');
@@ -6030,8 +6041,8 @@ total++;
6030
6041
  }
6031
6042
 
6032
6043
  // 1.9.395 회귀가드 (행위검증): audit README 배지 mismatch + current-state stale 의 FP/FN — 종전 전용 e2e 가드 부재
6033
- total++;
6034
- {
6044
+ if (e2eShard.take('B(1.9.395)')) {
6045
+ total++;
6035
6046
  let ok = false;
6036
6047
  try {
6037
6048
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-auditchk-'));
@@ -6060,8 +6071,8 @@ total++;
6060
6071
  }
6061
6072
 
6062
6073
  // 1.9.396 회귀 (6번째 외부평가/codex P1-B): task drop 없는 ID → fail + 가짜 row 무생성(데이터 손상 차단). 실제 task drop 은 정상.
6063
- total++;
6064
- {
6074
+ if (e2eShard.take('B(1.9.396)')) {
6075
+ total++;
6065
6076
  let ok = false;
6066
6077
  try {
6067
6078
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-taskdrop-'));
@@ -6083,8 +6094,8 @@ total++;
6083
6094
  }
6084
6095
 
6085
6096
  // 1.9.397 회귀 (6번째 외부평가/codex P1-A, UR-0098): install-safety 레시피 셸-무관(POSIX env-prefix 제거) + hardeningNote
6086
- total++;
6087
- {
6097
+ if (e2eShard.take('B(1.9.397)')) {
6098
+ total++;
6088
6099
  let ok = false;
6089
6100
  try {
6090
6101
  const r = cp.spawnSync(process.execPath, [CLI, 'install-safety', '--json'], { encoding: 'utf8', timeout: 10000 });
@@ -6099,8 +6110,8 @@ total++;
6099
6110
  }
6100
6111
 
6101
6112
  // 1.9.398 회귀 (6번째 외부평가/codex P1-C, UR-0099): --json 에러 경로가 구조화 JSON(텍스트 아님) + 사람용 보존
6102
- total++;
6103
- {
6113
+ if (e2eShard.take('B(1.9.398)')) {
6114
+ total++;
6104
6115
  let ok = false;
6105
6116
  try {
6106
6117
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-jsonerr-'));
@@ -6120,8 +6131,8 @@ total++;
6120
6131
  }
6121
6132
 
6122
6133
  // 1.9.399 회귀 (7번째 버그헌트 P1-A, UR-0104): 테이블셀 injection 차단 — task/rule 텍스트의 파이프(|) 보존 + 개행 가짜행 주입 차단 + rule 멱등성
6123
- total++;
6124
- {
6134
+ if (e2eShard.take('B(1.9.399)')) {
6135
+ total++;
6125
6136
  let ok = false;
6126
6137
  try {
6127
6138
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cellinj-'));
@@ -6146,8 +6157,8 @@ total++;
6146
6157
  }
6147
6158
 
6148
6159
  // 1.9.400 회귀 (7번째 버그헌트 P1-B, UR-0105): verify-claim/optimism-check/honesty-check --json 에러가 구조화 JSON + 사람용 보존
6149
- total++;
6150
- {
6160
+ if (e2eShard.take('B(1.9.400)')) {
6161
+ total++;
6151
6162
  let ok = false;
6152
6163
  try {
6153
6164
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-antilazyjson-'));
@@ -6164,8 +6175,8 @@ total++;
6164
6175
  }
6165
6176
 
6166
6177
  // 1.9.401 회귀 (7번째 버그헌트 P1-C, UR-0106): 시크릿 FN — gitignore 부정(!) 처리 + placeholder substring 정밀화
6167
- total++;
6168
- {
6178
+ if (e2eShard.take('B(1.9.401)')) {
6179
+ total++;
6169
6180
  let ok = false;
6170
6181
  try {
6171
6182
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-secfn-'));
@@ -6187,8 +6198,8 @@ total++;
6187
6198
  }
6188
6199
 
6189
6200
  // 1.9.402 회귀 (7번째 버그헌트 P1-A 잔여, UR-0108): decision/lesson 텍스트 개행이 MD projection 위조 블록 주입 차단
6190
- total++;
6191
- {
6201
+ if (e2eShard.take('B(1.9.402)')) {
6202
+ total++;
6192
6203
  let ok = false;
6193
6204
  try {
6194
6205
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mdinj-'));
@@ -6209,8 +6220,8 @@ total++;
6209
6220
  }
6210
6221
 
6211
6222
  // 1.9.403 회귀 (7번째 버그헌트 P2, UR-0107): api-skill show/drop 의 missing-id/not-found 에러가 exit 1(성공 오판 방지)
6212
- total++;
6213
- {
6223
+ if (e2eShard.take('B(1.9.403)')) {
6224
+ total++;
6214
6225
  let ok = false;
6215
6226
  try {
6216
6227
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-apiskillexit-'));
@@ -6229,8 +6240,8 @@ total++;
6229
6240
  }
6230
6241
 
6231
6242
  // 1.9.404 회귀 (7번째 버그헌트 P2, UR-0105 잔여): reuse autodetect / creds check 의 --json 에러도 구조화 JSON
6232
- total++;
6233
- {
6243
+ if (e2eShard.take('B(1.9.404)')) {
6244
+ total++;
6234
6245
  let ok = false;
6235
6246
  try {
6236
6247
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-jsonleak2-'));
@@ -6252,8 +6263,8 @@ total++;
6252
6263
  }
6253
6264
 
6254
6265
  // 1.9.405 회귀수정 (8번째 버그헌트, UR-0109): 1.9.401 looksReal 가드 FP — 긴 서술형 placeholder 가 실키로 오탐되던 것 차단
6255
- total++;
6256
- {
6266
+ if (e2eShard.take('B(1.9.405)')) {
6267
+ total++;
6257
6268
  let ok = false;
6258
6269
  try {
6259
6270
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-phfp-'));
@@ -6274,8 +6285,8 @@ total++;
6274
6285
  }
6275
6286
 
6276
6287
  // 1.9.406 회귀 (8번째 버그헌트, UR-0110): rule/decision/lesson add 락 리팩터가 멱등성/정확성 보존 (skip 플래그 회귀가드)
6277
- total++;
6278
- {
6288
+ if (e2eShard.take('B(1.9.406)')) {
6289
+ total++;
6279
6290
  let ok = false;
6280
6291
  try {
6281
6292
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lockrefactor-'));
@@ -6305,8 +6316,8 @@ total++;
6305
6316
  }
6306
6317
 
6307
6318
  // 1.9.407 회귀 (8번째 버그헌트, UR-0111): MCP feature_link safe-write tier + NaN --limit 가 결과 은폐 안 함
6308
- total++;
6309
- {
6319
+ if (e2eShard.take('B(1.9.407)')) {
6320
+ total++;
6310
6321
  let ok = false;
6311
6322
  try {
6312
6323
  // ① feature_link tier safe-write (권한경계)
@@ -6329,8 +6340,8 @@ total++;
6329
6340
  }
6330
6341
 
6331
6342
  // 1.9.408 회귀 (8번째 버그헌트, UR-0112): CRLF/CR SKILL.md frontmatter 파싱 — Windows/Notepad 줄바꿈으로 meta 소실되던 skill install 복구
6332
- total++;
6333
- {
6343
+ if (e2eShard.take('B(1.9.408)')) {
6344
+ total++;
6334
6345
  let ok = false;
6335
6346
  try {
6336
6347
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-crlfskill-'));
@@ -6354,8 +6365,8 @@ total++;
6354
6365
  // 1.9.409 회귀 (8번째 버그헌트, UR-0113): env encoding-check --apply 는 .sh/shebang 을 byte-exact 보존한다.
6355
6366
  // 1.36.177 데이터 무결성 계약: .ps1 BOM 교체는 Windows 에서만 메타데이터 보존 writer 로 수행하고,
6356
6367
  // POSIX 에서는 inode 교체가 ACL/xattr/ownership 을 잃을 수 있어 byte-exact no-op 한다.
6357
- total++;
6358
- {
6368
+ if (e2eShard.take('B(1.9.409/1.36.177)')) {
6369
+ total++;
6359
6370
  let ok = false;
6360
6371
  try {
6361
6372
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-bomsh-'));
@@ -6383,8 +6394,8 @@ total++;
6383
6394
  // 그 폴백이 바로 이번에 고친 결함이다 — `decision add SNEAKY --path=` 가 cwd 에 쓰고 "✓ 기록됨" 을 냈다(실측).
6384
6395
  // 플래그를 적었는데 값이 없으면 그건 오타다. 원래 목적(**raw TypeError 를 흘리지 않는다**)은 그대로 두고,
6385
6396
  // "조용한 폴백" 대신 "사유를 말하는 거절" 을 단언한다. 그리고 값을 준 경우가 여전히 도는지 대조군으로 잰다.
6386
- total++;
6387
- {
6397
+ if (e2eShard.take('B(1.9.410/1.36.144)')) {
6398
+ total++;
6388
6399
  let ok = false;
6389
6400
  try {
6390
6401
  const r = cp.spawnSync(process.execPath, [CLI, 'status', '--path'], { encoding: 'utf8', timeout: 15000 });
@@ -6402,8 +6413,8 @@ total++;
6402
6413
  }
6403
6414
 
6404
6415
  // 1.9.411 회귀 (8번째 버그헌트, UR-0115): lazy detect --auto-track 배치화가 다수 TODO 를 순차 ID 로 정확히 등록(동작 보존)
6405
- total++;
6406
- {
6416
+ if (e2eShard.take('B(1.9.411)')) {
6417
+ total++;
6407
6418
  let ok = false;
6408
6419
  try {
6409
6420
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-autotrack-'));
@@ -6426,8 +6437,8 @@ total++;
6426
6437
  }
6427
6438
 
6428
6439
  // 1.9.412 회귀 (6th외부평가 Opus P1, UR-0100): list-family 가 positional path 를 인식(조용한 cwd 오독 차단) + add/show 회귀 없음
6429
- total++;
6430
- {
6440
+ if (e2eShard.take('B(1.9.412)')) {
6441
+ total++;
6431
6442
  let ok = false;
6432
6443
  try {
6433
6444
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-pospath-'));
@@ -6451,8 +6462,8 @@ total++;
6451
6462
  // 1.36.2 회귀 (클린룸 리뷰, UR-0184): feature add/show/link/impact 도 trailing positional path 인식 —
6452
6463
  // 기존엔 add 가 모든 non-flag positional 을 NAME 으로 join → 경로가 이름에 흡수 + 비-프로젝트 cwd 에 stray .leerness scaffold(조용한 오독).
6453
6464
  // fix: --path > path-like positional > cwd; add 의 NAME 은 _parseAddTitle 로 절단; 미초기화 dir 은 _requireInit 게이트(scaffold 대신 에러).
6454
- total++;
6455
- {
6465
+ if (e2eShard.take('B(1.36.2)')) {
6466
+ total++;
6456
6467
  let ok = false;
6457
6468
  try {
6458
6469
  const target = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-featpos-t-'));
@@ -6488,8 +6499,8 @@ total++;
6488
6499
  }
6489
6500
 
6490
6501
  // 1.9.413 회귀 (6th외부평가 codex P2, UR-0101): action 명령 --json 구조화 출력 + 데이터 영속 + 사람용 보존
6491
- total++;
6492
- {
6502
+ if (e2eShard.take('B(1.9.413)')) {
6503
+ total++;
6493
6504
  let ok = false;
6494
6505
  try {
6495
6506
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-actionjson-'));
@@ -6513,8 +6524,8 @@ total++;
6513
6524
  }
6514
6525
 
6515
6526
  // 1.9.414 회귀 (UR-0119/0120): team review(메인 검수) — preview 가 분배 후 메인 검수 단계 표시, --no-review 시 생략
6516
- total++;
6517
- {
6527
+ if (e2eShard.take('B(1.9.414)')) {
6528
+ total++;
6518
6529
  let ok = false;
6519
6530
  try {
6520
6531
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-teamreview-'));
@@ -6537,8 +6548,8 @@ total++;
6537
6548
  }
6538
6549
 
6539
6550
  // 1.9.415 회귀 (9th 외부평가 Codex P1/Opus P2, UR-0121): handoff 보안 헤드라인 정직화 + scan/encoding --json + contract --json exit
6540
- total++;
6541
- {
6551
+ if (e2eShard.take('B(1.9.415)')) {
6552
+ total++;
6542
6553
  let ok = false;
6543
6554
  try {
6544
6555
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-honesty-'));
@@ -6572,8 +6583,8 @@ total++;
6572
6583
  }
6573
6584
 
6574
6585
  // 1.9.416 회귀 (9th 외부평가 Sonnet/Codex, UR-0122): add류 제목 경로흡수 차단 + 빈 입력 거부
6575
- total++;
6576
- {
6586
+ if (e2eShard.take('B(1.9.416)')) {
6587
+ total++;
6577
6588
  let ok = false;
6578
6589
  try {
6579
6590
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-addtitle-'));
@@ -6601,8 +6612,8 @@ total++;
6601
6612
  }
6602
6613
 
6603
6614
  // 1.9.417 회귀 (9th 외부평가 Opus, UR-0123): contract verify field 범용화 — ## Fields 섹션 불릿 누락 감지
6604
- total++;
6605
- {
6615
+ if (e2eShard.take('B(1.9.417)')) {
6616
+ total++;
6606
6617
  let ok = false;
6607
6618
  try {
6608
6619
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cfield-'));
@@ -6623,8 +6634,8 @@ total++;
6623
6634
  }
6624
6635
 
6625
6636
  // 1.35.11 (자체 contract 적대적 헌트 + codex 교차): 필드명 $ 정규식-앵커 FP — $scope/foo$bar 실존 필드를 항상 missing 오탐하던 버그. 이스케이프+식별자 룩어라운드로 수정. 정규식 안전 + no-FN 회귀 가드.
6626
- total++;
6627
- {
6637
+ if (e2eShard.take('B(1.35.11)')) {
6638
+ total++;
6628
6639
  let ok = false;
6629
6640
  try {
6630
6641
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cvdollar-'));
@@ -6661,8 +6672,8 @@ total++;
6661
6672
  }
6662
6673
 
6663
6674
  // 1.9.418 회귀 (9th 외부평가 Codex P2, UR-0121 잔여): health 보안 정직화 + status scope
6664
- total++;
6665
- {
6675
+ if (e2eShard.take('B(1.9.418)')) {
6676
+ total++;
6666
6677
  let ok = false;
6667
6678
  try {
6668
6679
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-hlabel-'));
@@ -6689,8 +6700,8 @@ total++;
6689
6700
  // 1.30.1 회귀 (14th 외부리뷰 F1+F2): audit/handoff 보안요약이 커밋된 시크릿을 정직하게 노출.
6690
6701
  // F1: audit 가 _collectSecretFindings 콘텐츠 스캔을 돌려 committed 시크릿을 failure 로 승격(scan secrets 와 일관) — gitignored 는 무영향(FP 0).
6691
6702
  // F2: handoff 🔒 보안요약 섹션이 .env 없어도 committed 시크릿을 노출(envExists 단독 게이팅 제거).
6692
- total++;
6693
- {
6703
+ if (e2eShard.take('B(1.30.1)')) {
6704
+ total++;
6694
6705
  let ok = false;
6695
6706
  try {
6696
6707
  const H = /[가-힣]/;
@@ -6731,8 +6742,8 @@ total++;
6731
6742
  }
6732
6743
 
6733
6744
  // 1.30.2 회귀 (#157 사용자명시, 하위 프로젝트 방향 — 외부AI+Claude 교차검토 → 방향 C): parent detect 가 상위 leerness 부모를 탐지(read-only) + handoff 헤드라인 노출 + 자동 적용 안 함.
6734
- total++;
6735
- {
6745
+ if (e2eShard.take('B(1.30.2)')) {
6746
+ total++;
6736
6747
  let ok = false;
6737
6748
  try {
6738
6749
  const par = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-par-'));
@@ -6765,8 +6776,8 @@ total++;
6765
6776
  }
6766
6777
 
6767
6778
  // 1.30.3 회귀 (#158 사용자명시): parent adopt 게이트형 적용 — dry-run 기본(쓰기 0) + --apply(사용자 명시) 시에만 자식-로컬 참조 기록 + 자식 design-system.md 무변경(비파괴) + handoff 헤드라인 adopted 반영.
6768
- total++;
6769
- {
6779
+ if (e2eShard.take('B(1.30.3)')) {
6780
+ total++;
6770
6781
  let ok = false;
6771
6782
  try {
6772
6783
  const par = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-adopt-'));
@@ -6800,8 +6811,8 @@ total++;
6800
6811
  }
6801
6812
 
6802
6813
  // 1.30.4 회귀 (#155 / 14th리뷰 F5+F6+F7): add류 cli-ux 일관성 — decision/lesson dedup + rule/lesson 빈입력 --json 구조화 + task/rule bogus subcommand 토큰 명시.
6803
- total++;
6804
- {
6814
+ if (e2eShard.take('B(1.30.4)')) {
6815
+ total++;
6805
6816
  let ok = false;
6806
6817
  try {
6807
6818
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-f567-'));
@@ -6835,8 +6846,8 @@ total++;
6835
6846
  }
6836
6847
 
6837
6848
  // 1.31.1 회귀 (UR-0010): install-safety 출력 영어 opt-in — en 한글 0 + ko 보존 + 셸-무관 가드(npx --yes/PowerShell/no npm_config prefix) 양 언어 보존.
6838
- total++;
6839
- {
6849
+ if (e2eShard.take('B(1.31.1)')) {
6850
+ total++;
6840
6851
  let ok = false;
6841
6852
  try {
6842
6853
  const H = /[가-힣]/;
@@ -6853,8 +6864,8 @@ total++;
6853
6864
  }
6854
6865
 
6855
6866
  // 1.31.2 회귀 (UR-0010): constraints 영어화 — list/check 라벨 + 카탈로그 detailEn + suggestion 영어 / ko 보존 / 매칭(한국어 alias) 무회귀.
6856
- total++;
6857
- {
6867
+ if (e2eShard.take('B(1.31.2)')) {
6868
+ total++;
6858
6869
  let ok = false;
6859
6870
  try {
6860
6871
  const H = /[가-힣]/;
@@ -6879,8 +6890,8 @@ total++;
6879
6890
  }
6880
6891
 
6881
6892
  // 1.31.3 회귀 (UR-0010): capabilities(권한·보안 표면) 영어화(라벨+카탈로그 descEn/optOutEn/noteEn+principles) + team reminder 본문 영어화 / ko 보존.
6882
- total++;
6883
- {
6893
+ if (e2eShard.take('B(1.31.3)')) {
6894
+ total++;
6884
6895
  let ok = false;
6885
6896
  try {
6886
6897
  const H = /[가-힣]/;
@@ -6913,8 +6924,8 @@ total++;
6913
6924
  }
6914
6925
 
6915
6926
  // 1.32.1 회귀 (15th 외부리뷰 맹신X): constraints/parent --json 에러 구조화(C2) + parent adopt --json 에러경로 비공백(A1) + --select 무효 kind applied:false(A2).
6916
- total++;
6917
- {
6927
+ if (e2eShard.take('B(1.32.1)')) {
6928
+ total++;
6918
6929
  let ok = false;
6919
6930
  try {
6920
6931
  const isJson = s => { try { JSON.parse(String(s).trim()); return true; } catch { return false; } };
@@ -6949,8 +6960,8 @@ total++;
6949
6960
 
6950
6961
  // 1.32.3 회귀 (15th 리뷰 A3 방어심화): 적대적 부모 design-system content(가짜 ## 헤더 + 가짜 leerness 마커 + 백틱런)가
6951
6962
  // parent adopt --apply 시 자식 inherited-from-parent.md 에 동적 코드펜스로 감싸져 marker/헤더 spoofing 차단되는지 + content 보존.
6952
- total++;
6953
- {
6963
+ if (e2eShard.take('B(1.32.3)')) {
6964
+ total++;
6954
6965
  let ok = false;
6955
6966
  try {
6956
6967
  const base = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-a3-'));
@@ -6980,8 +6991,8 @@ total++;
6980
6991
  }
6981
6992
 
6982
6993
  // 1.33.1 회귀 (verify-claim+gate 슬라이스 강화): ci init 생성 워크플로가 production-grade — leerness 버전 핀(재현성) + 최소권한 permissions + concurrency 취소.
6983
- total++;
6984
- {
6994
+ if (e2eShard.take('B(1.33.1)')) {
6995
+ total++;
6985
6996
  let ok = false;
6986
6997
  try {
6987
6998
  const ver = require(path.resolve(__dirname, '..', 'package.json')).version;
@@ -7006,8 +7017,8 @@ total++;
7006
7017
  }
7007
7018
 
7008
7019
  // 1.33.2 회귀 (verify-claim+gate 슬라이스 강화): verify-claim --all — 모든 done 주장 일괄 검증. 거짓완료=exit 1(files-missing), 진실완료/빈프로젝트=exit 0, per-task 경로와 verdict 일치(맹신 X).
7009
- total++;
7010
- {
7020
+ if (e2eShard.take('B(1.33.2)')) {
7021
+ total++;
7011
7022
  let ok = false;
7012
7023
  try {
7013
7024
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vca-'));
@@ -7046,8 +7057,8 @@ total++;
7046
7057
  }
7047
7058
 
7048
7059
  // 1.33.3 회귀 (verify-claim+CI gate 슬라이스 강화): gate --claims opt-in 6번째 체크(기본 5 무변경) + MCP leerness_verify_claim_all 라운드트립.
7049
- total++;
7050
- {
7060
+ if (e2eShard.take('B(1.33.3)')) {
7061
+ total++;
7051
7062
  let ok = false;
7052
7063
  try {
7053
7064
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-g3-'));
@@ -7086,8 +7097,8 @@ total++;
7086
7097
  }
7087
7098
 
7088
7099
  // 1.34.1 회귀 (16th리뷰 정직화 실증): gate --claims 정밀성 가치 — 워크스페이스가 깨끗(lazy detect 0 finding)한데 콘텐츠-레벨 거짓(부풀린 테스트 카운트)인 경우, 기본 5체크는 통과(exit 0)하고 --claims 만 차단(exit 1). 이 판별 케이스가 깨지면(기본도 잡거나 --claims가 못 잡으면) --claims 의 차별 가치가 사라진 것이므로 가드.
7089
- total++;
7090
- {
7100
+ if (e2eShard.take('B(1.34.1)')) {
7101
+ total++;
7091
7102
  let ok = false;
7092
7103
  try {
7093
7104
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-disc-'));
@@ -7124,8 +7135,8 @@ total++;
7124
7135
  // stubFiles=[] → implementationSubstance=true, gitApplicable 은 files>0 요구 → skip.
7125
7136
  // 전 판정이 공허참이라 "코드도 테스트도 없이 done" 이 ok:true/exit 0 이었고, 단조성이 뒤집혀 있었다
7126
7137
  // (파일명을 적어 검증 가능하게 쓸수록 FAIL, 날조할수록 PASS). 판별 케이스로 고정한다.
7127
- total++;
7128
- {
7138
+ if (e2eShard.take('B(1.36.82)')) {
7139
+ total++;
7129
7140
  let ok = false;
7130
7141
  try {
7131
7142
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-vac-'));
@@ -7185,8 +7196,8 @@ total++;
7185
7196
 
7186
7197
  // 1.36.82 회귀 (High #2): `gate --require-referee <id>` — 툴이 사용자에게 안내하면서 gate 는 읽지도 않아
7187
7198
  // 조용히 exit 0 이던 플래그. 이제 실제 게이팅한다(미존재/미증명/무효 거부 + 검증기가 현재 상태를 거부하면 실패).
7188
- total++;
7189
- {
7199
+ if (e2eShard.take('B(1.36.82) #2')) {
7200
+ total++;
7190
7201
  let ok = false;
7191
7202
  try {
7192
7203
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-refgate-'));
@@ -7232,8 +7243,8 @@ total++;
7232
7243
  }
7233
7244
 
7234
7245
  // 1.35.10 (자체 gate 적대적 헌트): gate 를 "보안 가드레일"로 검증 — 8-프로브 헌트(FP 4 + FN 3 + by-design 1) 결과 제품 결함 0 확인 후, 유일 커버리지 갭(gate 레벨 보안 동작)을 회귀 가드로 고정. 커밋된 실키 → 차단(exit 1, scan secrets step), 정상 .env.example placeholder → 오탐 없이 통과(exit 0). scan-step 리팩터가 가드레일 보안을 조용히 무너뜨리는 회귀 차단.
7235
- total++;
7236
- {
7246
+ if (e2eShard.take('B(1.35.10)')) {
7247
+ total++;
7237
7248
  let ok = false;
7238
7249
  try {
7239
7250
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-gatesec-'));
@@ -7257,8 +7268,8 @@ total++;
7257
7268
  }
7258
7269
 
7259
7270
  // 1.9.430 (10th 외부평가 UR-0130): health 보안 CRITICAL(커밋 시크릿)은 --strict 없이도 exit 1(CI 게이트). 클린은 exit 0.
7260
- total++;
7261
- {
7271
+ if (e2eShard.take('B(1.9.430)')) {
7272
+ total++;
7262
7273
  let ok = false;
7263
7274
  try {
7264
7275
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-hexit-'));
@@ -7274,8 +7285,8 @@ total++;
7274
7285
  }
7275
7286
 
7276
7287
  // 1.9.435 (11th 외부평가 Codex P2, UR-0137): agents dispatch task 에 flag 값(--to 의 codex) 흡수 금지.
7277
- total++;
7278
- {
7288
+ if (e2eShard.take('B(1.9.435/1.35.6)')) {
7289
+ total++;
7279
7290
  let ok = false;
7280
7291
  let d = null;
7281
7292
  let fbin = null;
@@ -7318,8 +7329,8 @@ total++;
7318
7329
  }
7319
7330
 
7320
7331
  // 1.9.439 (10th 외부평가 Codex P1, UR-0135): drift --auto-fix --json 은 dirty WS 에서도 stdout 순수 JSON.
7321
- total++;
7322
- {
7332
+ if (e2eShard.take('B(1.9.439)')) {
7333
+ total++;
7323
7334
  let ok = false;
7324
7335
  try {
7325
7336
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-djson-'));
@@ -7336,8 +7347,8 @@ total++;
7336
7347
  }
7337
7348
 
7338
7349
  // 1.9.440 (12th 외부평가 Opus P2): 시크릿 스캐너 prefix 패턴(AWS/GitHub)도 placeholder 가드 — .env.example 더미는 미탐, 진짜는 탐지.
7339
- total++;
7340
- {
7350
+ if (e2eShard.take('B(1.9.440)')) {
7351
+ total++;
7341
7352
  let ok = false;
7342
7353
  try {
7343
7354
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-scanph-'));
@@ -7356,8 +7367,8 @@ total++;
7356
7367
  }
7357
7368
 
7358
7369
  // 1.9.442 (12th 외부평가 Sonnet P1, UR-0141): task 계열 positional path — 다른 cwd 에서 실행해도 positional 경로에 저장(cwd 오염 차단).
7359
- total++;
7360
- {
7370
+ if (e2eShard.take('B(1.9.442)')) {
7371
+ total++;
7361
7372
  let ok = false;
7362
7373
  try {
7363
7374
  const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-taskpos-'));
@@ -7376,8 +7387,8 @@ total++;
7376
7387
  }
7377
7388
 
7378
7389
  // 1.9.443 (GPT-5.5 전략리뷰 §6.3, UR-0153): evidence-first 완료 게이트 — state 워크플로에서 completion_claim_allowed 파생.
7379
- total++;
7380
- {
7390
+ if (e2eShard.take('B(1.9.443)')) {
7391
+ total++;
7381
7392
  let ok = false;
7382
7393
  try {
7383
7394
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cca-'));
@@ -7398,8 +7409,8 @@ total++;
7398
7409
  }
7399
7410
 
7400
7411
  // 1.9.444 (GPT-5.5 전략리뷰 §6.7, UR-0152): ci init — PR gate 워크플로 생성(멱등).
7401
- total++;
7402
- {
7412
+ if (e2eShard.take('B(1.9.444)')) {
7413
+ total++;
7403
7414
  let ok = false;
7404
7415
  try {
7405
7416
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ci-'));
@@ -7419,8 +7430,8 @@ total++;
7419
7430
  }
7420
7431
 
7421
7432
  // 1.9.445 (UR-0151): add-family(decision/lesson/rule) positional path — 다른 cwd 에서 실행해도 positional 경로에 저장(cwd 오염 차단).
7422
- total++;
7423
- {
7433
+ if (e2eShard.take('B(1.9.445)')) {
7434
+ total++;
7424
7435
  let ok = false;
7425
7436
  try {
7426
7437
  const ws = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-addpos-'));
@@ -7446,8 +7457,8 @@ total++;
7446
7457
 
7447
7458
  // 1.25.1 (22nd 버그헌트 → i18n 행위 회귀 가드, UR-0010): --language en 런타임 렌더가 실제로 영어인지 + ko 기본 보존 + --language 값이 positional 로 누출 안 되는지 행위로 검증.
7448
7459
  // 소스가드(문자열 존재)만으로는 1.23.0 "session close 완전 영어" 과장(런타임 한글 누출)을 못 잡았던 공백을 e2e 로 보강(defense-in-depth).
7449
- total++;
7450
- {
7460
+ if (e2eShard.take('B(1.25.1/1.25.2/1.27.2/1.28.2/1.29.1/1.29.2/1.29.3/1.29.4/1.30.5)')) {
7461
+ total++;
7451
7462
  let ok = false;
7452
7463
  try {
7453
7464
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-i18n-'));
@@ -7576,8 +7587,8 @@ total++;
7576
7587
  }
7577
7588
 
7578
7589
  // 1.26.1 (13번째 외부리뷰 P2 회귀가드): 개인키파일 스캔 FN + DB placeholder FP + retro --json NaN 행위 가드.
7579
- total++;
7580
- {
7590
+ if (e2eShard.take('B(1.26.1)')) {
7591
+ total++;
7581
7592
  let ok = false;
7582
7593
  try {
7583
7594
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rev13-'));
@@ -7609,8 +7620,8 @@ total++;
7609
7620
  }
7610
7621
 
7611
7622
  // 1.27.1 (13번째 외부리뷰 정직성 후속 회귀가드): audit 미초기화 모순출력 차단 + verify-claim no-parse 정직표기 (양방향 무회귀).
7612
- total++;
7613
- {
7623
+ if (e2eShard.take('B(1.27.1)')) {
7624
+ total++;
7614
7625
  let ok = false;
7615
7626
  try {
7616
7627
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rev13b-'));
@@ -7644,8 +7655,8 @@ total++;
7644
7655
  }
7645
7656
 
7646
7657
  // 1.35.7 (UR-0013, GPT5.5pro 평가): declared-pass 부풀림 게이팅 — 주장(3/3) vs 실행(2/2) → exit 1 (human/--json/--all 3경로) + spec 리포터 파싱(패턴 6) + 실행≥주장 무벌점.
7647
- total++;
7648
- {
7658
+ if (e2eShard.take('B(1.35.7)')) {
7659
+ total++;
7649
7660
  let ok = false;
7650
7661
  try {
7651
7662
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-dpm-'));
@@ -7687,8 +7698,8 @@ total++;
7687
7698
 
7688
7699
  // 1.36.1 회귀 (클린룸 리뷰 FN): 상태파일 JSON 무결성 — 손상 .leerness/*.json 을 audit(warning)/health(degraded)/check(exit 1) 가 표면화.
7689
7700
  // 배경: 그레이스풀 폴백이 깨진 상태 JSON 을 "healthy"/exit 0 으로 감추던 false-negative(health/doctor/check 전부). 클린(유효 JSON)엔 무오탐.
7690
- total++;
7691
- {
7701
+ if (e2eShard.take('B(1.36.1)')) {
7702
+ total++;
7692
7703
  let ok = false;
7693
7704
  try {
7694
7705
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-si-'));
@@ -7720,8 +7731,8 @@ total++;
7720
7731
  }
7721
7732
 
7722
7733
  // 1.36.51 (사용자 요청 UR-0061): clarify 모호성 질문 + preview 승인 워크플로 — CLI 라운드트립
7723
- total++;
7724
- {
7734
+ if (e2eShard.take('B(1.36.51)')) {
7735
+ total++;
7725
7736
  let ok = false;
7726
7737
  try {
7727
7738
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-clarify-'));
@@ -7754,8 +7765,8 @@ total++;
7754
7765
  }
7755
7766
 
7756
7767
  // 1.36.53 (사용자 요청 UR-0062): tech 프로필 — 언어·서비스 감지/이력 + 그래프 🛠 탭(스위치 제거·가이드) CLI 라운드트립
7757
- total++;
7758
- {
7768
+ if (e2eShard.take('B(1.36.53)')) {
7769
+ total++;
7759
7770
  let ok = false;
7760
7771
  try {
7761
7772
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tech-'));
@@ -7782,8 +7793,8 @@ total++;
7782
7793
  }
7783
7794
 
7784
7795
  // 1.36.59 (외부감사 F-05 1회차): en init 의 지시-레이어 5종은 한글 0 + ko 완전 보존
7785
- total++;
7786
- {
7796
+ if (e2eShard.take('B(1.36.59)')) {
7797
+ total++;
7787
7798
  let ok = false;
7788
7799
  const _dirs = [];
7789
7800
  try {
@@ -7816,8 +7827,8 @@ total++;
7816
7827
  }
7817
7828
 
7818
7829
  // 1.36.65 (외부감사 F-08): no-op 재설치 — 변경 없으면 백업/아카이브/타임스탬프 무증식, 변경·구버전·force 는 정상 설치
7819
- total++;
7820
- {
7830
+ if (e2eShard.take('B(1.36.65)')) {
7831
+ total++;
7821
7832
  let ok = false;
7822
7833
  const _d = [];
7823
7834
  try {
@@ -7848,8 +7859,8 @@ total++;
7848
7859
  }
7849
7860
 
7850
7861
  // 1.36.68 (8차 헌트 F14 재구현): 마감 보고/JSON 에 미승인 미리보기 노출 — 텍스트·JSON 양 모드 + 승인 후 소거
7851
- total++;
7852
- {
7862
+ if (e2eShard.take('B(1.36.68)')) {
7863
+ total++;
7853
7864
  let ok = false;
7854
7865
  const _d = [];
7855
7866
  try {
@@ -7878,8 +7889,8 @@ total++;
7878
7889
  }
7879
7890
 
7880
7891
  // 1.36.69 (성장 한계 클래스): whats-new --json 기본 상한 — AI 컨텍스트 보호 + 정직한 절단 표기 + --all/--limit 옵트인
7881
- total++;
7882
- {
7892
+ if (e2eShard.take('B(1.36.69)')) {
7893
+ total++;
7883
7894
  let ok = false;
7884
7895
  try {
7885
7896
  const R = (a) => cp.spawnSync(process.execPath, [CLI, 'whats-new', ...a], { encoding: 'utf8', timeout: 25000, maxBuffer: 32 * 1024 * 1024 });
@@ -7935,8 +7946,8 @@ total++;
7935
7946
  }
7936
7947
 
7937
7948
  // 1.36.70 (성장 한계 클래스): migrate 보고서 상한 — 큰 버전 점프에서 ~88KB 폭주하던 AI must re-read 를 목록별 캡 + "외 N개" 정직 표기
7938
- total++;
7939
- {
7949
+ if (e2eShard.take('B(1.36.70)')) {
7950
+ total++;
7940
7951
  let ok = false;
7941
7952
  const _d = [];
7942
7953
  try {
@@ -7966,8 +7977,8 @@ total++;
7966
7977
  }
7967
7978
 
7968
7979
  // 1.36.71 (성장 한계 클래스 3): retro/insights --json data.rows 상한 — 전 task row 임베드로 무한 성장하던 것 (집계는 전량 기준 유지)
7969
- total++;
7970
- {
7980
+ if (e2eShard.take('B(1.36.71)')) {
7981
+ total++;
7971
7982
  let ok = false;
7972
7983
  const _d = [];
7973
7984
  try {
@@ -7998,8 +8009,8 @@ total++;
7998
8009
  }
7999
8010
 
8000
8011
  // 1.36.72 (성장 한계 클래스 4): task list --json 상한 100 + lint 거짓 PASS(F16) 차단
8001
- total++;
8002
- {
8012
+ if (e2eShard.take('B(1.36.72)')) {
8013
+ total++;
8003
8014
  let ok = false;
8004
8015
  const _d = [];
8005
8016
  try {
@@ -8026,8 +8037,8 @@ total++;
8026
8037
  }
8027
8038
 
8028
8039
  // 1.36.73 (8차 헌트 F12/F9 종결): skills-lock = 디스크 실재 + KO-잔존 이월 안내
8029
- total++;
8030
- {
8040
+ if (e2eShard.take('B(1.36.73)')) {
8041
+ total++;
8031
8042
  let ok = false;
8032
8043
  const _d = [];
8033
8044
  try {
@@ -8065,8 +8076,8 @@ total++;
8065
8076
  }
8066
8077
 
8067
8078
  // 1.36.74 (9차 헌트): state 스키마 fail-closed · restore 중복 거부 · api-skill skeleton 충돌 · MCP 마지막 줄 · 손상 표시 · --json 계약
8068
- total++;
8069
- {
8079
+ if (e2eShard.take('B(1.36.74)')) {
8080
+ total++;
8070
8081
  let ok = false;
8071
8082
  const _d = [];
8072
8083
  try {
@@ -8140,8 +8151,8 @@ total++;
8140
8151
  }
8141
8152
 
8142
8153
  // 1.36.75 (UR-0066): 디자인 시안 워크플로 — preview mockup 스캐폴드 + 덮어쓰기 보호 + XSS 이스케이프 + 디자인 감지 + --mockup 첨부
8143
- total++;
8144
- {
8154
+ if (e2eShard.take('B(1.36.75)')) {
8155
+ total++;
8145
8156
  let ok = false;
8146
8157
  const _d = [];
8147
8158
  try {
@@ -8200,8 +8211,8 @@ total++;
8200
8211
  // selftest 는 checkDoneTransition 을 in-process 로만 불러, `task update --status done` 배선을 한 줄도
8201
8212
  // 실행하지 않았다 — 게이트를 통째로 죽여도 341/341 그린이었다(변이로 실측). 실제 CLI 경로로 못 박는다.
8202
8213
  // 특히 done 을 기록하는 경로가 taskUpdate 하나가 아니다: task sync --from(TodoWrite 왕복)이 우회했다.
8203
- total++;
8204
- {
8214
+ if (e2eShard.take('B(1.36.88) #2')) {
8215
+ total++;
8205
8216
  let ok = false;
8206
8217
  const _d = [];
8207
8218
  const R = (args, cwd) => { const r = cp.spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', timeout: 180000, cwd }); return { s: r.status, o: ((r.stdout || '') + (r.stderr || '')).trim() }; };
@@ -8272,8 +8283,8 @@ total++;
8272
8283
  // 1.36.89 (P-0007, 사용자 승인 범위=제안+확인후실행): agents route 난이도 라우팅.
8273
8284
  // 판별 케이스를 함께 넣는다 — 거부 사유(code)까지 단언하지 않으면 여러 거부 조건이 서로를 가려
8274
8285
  // 하나를 죽여도 다른 하나가 막는다(1.36.88 에서 변이 6/8 로 실측한 함정).
8275
- total++;
8276
- {
8286
+ if (e2eShard.take('B(1.36.89)')) {
8287
+ total++;
8277
8288
  let ok = false;
8278
8289
  const _d = [];
8279
8290
  const R = (args, cwd) => { const r = cp.spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', timeout: 120000, cwd }); return { s: r.status, o: ((r.stdout || '') + (r.stderr || '')).trim() }; };
@@ -8401,8 +8412,8 @@ total++;
8401
8412
  // 1.36.90 (P-0006, 사용자 승인 범위=대시보드만): 읽기 전용 대시보드 + **넘지 않기로 한 경계**를 가드로 못 박는다.
8402
8413
  // 경계가 문서에만 있으면 다음 라운드에 조용히 넘는다 — .leerness 실행코드 0 · 루트 실행기 미생성 ·
8403
8414
  // 게이트 미판정 · 정적 leerness.html 존속을 테스트가 강제한다.
8404
- total++;
8405
- {
8415
+ if (e2eShard.take('B(1.36.90)')) {
8416
+ total++;
8406
8417
  let ok = false;
8407
8418
  const _d = [];
8408
8419
  const R = (args, cwd) => { const r = cp.spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', timeout: 120000, cwd }); return { s: r.status, o: ((r.stdout || '') + (r.stderr || '')).trim() }; };
@@ -8483,8 +8494,8 @@ total++;
8483
8494
  // 1.36.91: **설치됨 ≠ 사용가능** — 인증 축. 확인 가능한 provider 만 판정하고 나머지는 unknown 으로 남긴다.
8484
8495
  // unknown 을 차단 사유로 쓰면 확인 수단이 없는 대부분의 CLI 에서 고위험 작업이 전부 잠긴다(false-BLOCK) —
8485
8496
  // "확실히 아닌 것"만 막는다는 비대칭이 이 축의 핵심이고, 그걸 테스트가 못 박는다.
8486
- total++;
8487
- {
8497
+ if (e2eShard.take('B(1.36.91)')) {
8498
+ total++;
8488
8499
  let ok = false;
8489
8500
  const _d = [];
8490
8501
  const R = (args) => { const r = cp.spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', timeout: 120000 }); return { s: r.status, o: ((r.stdout || '') + (r.stderr || '')).trim() }; };
@@ -8600,8 +8611,8 @@ total++;
8600
8611
  // 1.36.92 (신규표면 헌트 확정 5건): 동의 없는 외부 CLI 실행 · 사유 오귀인 · 다국어 과소평가 ·
8601
8612
  // 위치경로 무시 · Bearer 토큰 미마스킹 · 손상 토글 무경고.
8602
8613
  // **PATH shim** 으로 "프로세스를 띄우지 않는다"를 증명한다 — 종전 테스트에는 그걸 증명할 수단이 아예 없었다.
8603
- total++;
8604
- {
8614
+ if (e2eShard.take('B(1.36.92)')) {
8615
+ total++;
8605
8616
  let ok = false;
8606
8617
  const _d = [];
8607
8618
  let N1 = false, N2 = false, N3 = false, N4 = false, N5 = false;
@@ -8689,8 +8700,8 @@ total++;
8689
8700
  // 1.36.93 (헌트 이월 2건): ① MCP 래퍼 타임아웃이 probe 예산보다 짧아 정직하게 고친 사용자가 done 을 못 하고,
8690
8701
  // 응답이 `(no output)` 이라 진단 단서가 0 이었다 ② `plan add --status verified` 가 정규화 없이 저장돼
8691
8702
  // lazy detect 는 done 으로 세는데 verify-claim/gate 는 그 행을 아예 보지 않았다(검증기가 못 보는 완료 행).
8692
- total++;
8693
- {
8703
+ if (e2eShard.take('B(1.36.93)')) {
8704
+ total++;
8694
8705
  let ok = false;
8695
8706
  const _d = [];
8696
8707
  const R = (args, env) => { const r = cp.spawnSync(process.execPath, [CLI, ...args], { encoding: 'utf8', timeout: 300000, env: env || process.env }); return { s: r.status, o: ((r.stdout || '') + (r.stderr || '')).trim() }; };
@@ -8755,8 +8766,8 @@ total++;
8755
8766
  // 검수가 9회에 걸쳐 사용자 데이터 삭제/변형 반례를 계속 찾아냈다 — 문자열로는 "우리가 내보낸 줄"과
8756
8767
  // "사용자가 그 줄을 적은 것"을 구분할 수 없기 때문이다(바이트가 같다). 근본 해결은 생성 영역 마커
8757
8768
  // (provenance)이고 별도 라운드로 다룬다. 여기서는 **삭제하지 않는 변경만** 출하한다.
8758
- total++;
8759
- {
8769
+ if (e2eShard.take('B(1.36.95)')) {
8770
+ total++;
8760
8771
  let ok = false;
8761
8772
  const _d = [];
8762
8773
  let T1 = false, T2 = false, T3 = false;
@@ -8913,8 +8924,8 @@ total++;
8913
8924
  // 이 블록이 잡아야 하는 것(전부 변이로 확인): 원시 HTML 통과 · javascript:/data: 링크 · 표/제목/링크텍스트
8914
8925
  // 미이스케이프 · 코드펜스 안 마크다운 해석 · 코드스팬 자리표시자가 본문 숫자를 삼킴 · 시크릿 미마스킹 ·
8915
8926
  // 누적 로그 방향 뒤집힘 · 줄경계/코드포인트 절단 붕괴 · 전체 상한에서 조용한 누락.
8916
- total++;
8917
- {
8927
+ if (e2eShard.take('B(1.36.96)')) {
8928
+ total++;
8918
8929
  let ok = false;
8919
8930
  const _d = [];
8920
8931
  const dbg = {};
@@ -9369,8 +9380,8 @@ total++;
9369
9380
  // 자연 경합 19.5% · 읽기 실패를 손상으로 오인 · <ID> 무시 전역 삭제 · 되돌릴 수 없는 게이트 무장해제 ·
9370
9381
  // 안내가 --path 를 잃어 애먼 프로젝트 파괴). 손으로 고치는 회복은 그 다섯이 전부 없고 되돌리면 게이트도
9371
9382
  // 살아난다 — 그래서 도구가 할 일은 대신 지워 주는 게 아니라 **어디를 어떻게 고치면 되는지 말하는 것**이다.
9372
- total++;
9373
- {
9383
+ if (e2eShard.take('B(1.36.94/1.36.185)')) {
9384
+ total++;
9374
9385
  let ok = false;
9375
9386
  const _d = [];
9376
9387
  let E1 = false, E2 = false, E3 = false, E4 = false, E5 = false;
@@ -9632,8 +9643,8 @@ s.listen(0,'127.0.0.1',()=>fs.writeFileSync(process.argv[3],String(s.address().p
9632
9643
  }
9633
9644
 
9634
9645
  // 1.36.76 (9차 헌트 이월 3건 종결): 손상 아카이브 복원 거부 · MCP read-only 무변형 · 긴 파생 id 안전화
9635
- total++;
9636
- {
9646
+ if (e2eShard.take('B(1.36.76)')) {
9647
+ total++;
9637
9648
  let ok = false;
9638
9649
  const _d = [];
9639
9650
  try {
@@ -9671,8 +9682,8 @@ total++;
9671
9682
  }
9672
9683
 
9673
9684
  // 1.36.77 (UR-0061/0066 MCP 완결): leerness_clarify + leerness_preview 도구 — MCP 전용 에이전트도 질문·시안 승인 흐름
9674
- total++;
9675
- {
9685
+ if (e2eShard.take('B(1.36.77)')) {
9686
+ total++;
9676
9687
  let ok = false;
9677
9688
  const _d = [];
9678
9689
  try {
@@ -9706,8 +9717,8 @@ total++;
9706
9717
  }
9707
9718
 
9708
9719
  // 1.36.78 (10차 헌트 확정 7건): team 형상/계약 · constraints 검증/손상 · agents recommend · 빈 contract
9709
- total++;
9710
- {
9720
+ if (e2eShard.take('B(1.36.78)')) {
9721
+ total++;
9711
9722
  let ok = false;
9712
9723
  const _d = [];
9713
9724
  try {
@@ -9775,8 +9786,8 @@ total++;
9775
9786
  }
9776
9787
 
9777
9788
  // 1.36.79 (실 프로젝트 60개 도그푸딩 P1 5건): progress 행 보존 · handoff --json 순수 · parent path · tech 언어 순위 · audit=integrity 정합
9778
- total++;
9779
- {
9789
+ if (e2eShard.take('B(1.36.79)')) {
9790
+ total++;
9780
9791
  let ok = false;
9781
9792
  const _d = [];
9782
9793
  try {
@@ -9862,8 +9873,8 @@ total++;
9862
9873
  }
9863
9874
 
9864
9875
  // 1.36.80 (P-0001 검증기 캘리브레이션): 검증기를 신뢰하기 전에 탐지력을 실행으로 증명 — 적대적 FP 프로브 포함
9865
- total++;
9866
- {
9876
+ if (e2eShard.take('B(1.36.80)')) {
9877
+ total++;
9867
9878
  let ok = false;
9868
9879
  const _d = [];
9869
9880
  try {
@@ -9950,8 +9961,8 @@ total++;
9950
9961
  }
9951
9962
 
9952
9963
  // 1.36.80 (UR-0067 라이브 미리보기 + P-0003 사다리 + 기본 스킬 정리)
9953
- total++;
9954
- {
9964
+ if (e2eShard.take('B(1.36.80) #2')) {
9965
+ total++;
9955
9966
  let ok = false;
9956
9967
  const _d = [];
9957
9968
  try {
@@ -9994,7 +10005,7 @@ total++;
9994
10005
  }
9995
10006
 
9996
10007
  // ── 1.36.97 (P-0011): 렌즈 3축 심화 — CLI 표면 행위. selftest 는 함수만 보므로 실제 렌더/로케일/완료검증 인라인은 여기서 잰다.
9997
- {
10008
+ if (e2eShard.take('B(1.36.97/P-0011)')) {
9998
10009
  total++;
9999
10010
  let ok = false; const dbg = {};
10000
10011
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lens97-'));
@@ -10049,7 +10060,7 @@ total++;
10049
10060
 
10050
10061
  // ── 1.36.98: session close 가 current-state.md 의 사용자 작성 내용을 지우지 않는가 (사용자 보고 재현 → 실 CLI 로 고정)
10051
10062
  // selftest 는 순수 함수만 본다. 손실은 '명령을 실제로 돌렸을 때' 났으므로 여기서 실 CLI 로 건다.
10052
- {
10063
+ if (e2eShard.take('B(1.36.98)')) {
10053
10064
  total++;
10054
10065
  let ok = false; const dbg = {};
10055
10066
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-cs98-'));
@@ -10083,7 +10094,7 @@ total++;
10083
10094
  }
10084
10095
 
10085
10096
  // ── 1.36.98 (P-0013): 재사용 인벤토리 CLI — 추출 결과가 아니라 '무엇을 싣지 않는가' 를 건다.
10086
- {
10097
+ if (e2eShard.take('B(1.36.98/P-0013)')) {
10087
10098
  total++;
10088
10099
  let ok = false; const dbg = {};
10089
10100
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lib98-'));
@@ -10121,7 +10132,7 @@ total++;
10121
10132
  }
10122
10133
 
10123
10134
  // ── 1.36.99 (P-0012): 시안 정확도 — 시안이 '빈 종이' 가 아니라 이 앱의 재료로 시작하는가.
10124
- {
10135
+ if (e2eShard.take('B(1.36.99/P-0012)')) {
10125
10136
  total++;
10126
10137
  let ok = false; const dbg = {};
10127
10138
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mk99-'));
@@ -10160,7 +10171,7 @@ total++;
10160
10171
  }
10161
10172
 
10162
10173
  // ── 1.36.100 (P-0012 후반): 승인 시 시안을 계약으로 고정 → 구현 후 존재 대조. 과장하지 않는 것까지 함께 건다.
10163
- {
10174
+ if (e2eShard.take('B(1.36.100/P-0012)')) {
10164
10175
  total++;
10165
10176
  let ok = false; const dbg = {};
10166
10177
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ct100-'));
@@ -10210,7 +10221,7 @@ total++;
10210
10221
  // ── 1.36.103 A: 값-플래그의 값이 사용자 데이터/경로로 흡수되지 않는다.
10211
10222
  // 허용목록이 소비자보다 45개 뒤처져 있었고, 같은 클래스의 목록이 다섯 벌로 갈라져 각자 따로 뒤처졌다.
10212
10223
  // 실측 재현: `preview add "제목" --select X` → 저장 제목 「제목 X」 · `review --persona security f.js` → 파일이 "security".
10213
- {
10224
+ if (e2eShard.take('A(1.36.103)')) {
10214
10225
  total++;
10215
10226
  let ok = false; const dbg = {};
10216
10227
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-fl103-'));
@@ -10256,7 +10267,7 @@ total++;
10256
10267
 
10257
10268
  // ── 1.36.103 B: --json 은 실패해도 stdout 에 파싱 가능한 JSON 하나를 남긴다.
10258
10269
  // 위반 5건이 서로 다른 이유였다 — 경고가 stdout 선점 / 실패 분기에 JSON 없음 / 출력을 삼키는 구간에서 표식만 서서 폴백 차단.
10259
- {
10270
+ if (e2eShard.take('B(1.36.103)')) {
10260
10271
  total++;
10261
10272
  let ok = false; const dbg = {};
10262
10273
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-js103-'));
@@ -10302,7 +10313,7 @@ total++;
10302
10313
  // 1.36.101 의 path-strict 가 args[1] 을 무조건 경로로 봐서 `leerness library page` 를 거부했고(게시된 1.36.102
10303
10314
  // 에서 확인), 같은 두 명령이 root 해석은 index 2 부터 시작해 검증된 경로를 건너뛰고 cwd 에 썼다.
10304
10315
  // 관측값은 파일시스템이다 — 문구가 아니라 '어디에 썼는지'.
10305
- {
10316
+ if (e2eShard.take('C(1.36.103)')) {
10306
10317
  total++;
10307
10318
  let ok = false; const dbg = {};
10308
10319
  const arena = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rt103-'));
@@ -10338,7 +10349,7 @@ total++;
10338
10349
  // _bumpMcpUsage 는 stats.mcp.tools 에 기록하는데 usage 표시는 commands 가 비면 이른 return 이라
10339
10350
  // 그 아래 MCP 섹션에 도달하지 못했다 → MCP 로만 일하는 에이전트의 프로젝트가 "사용 기록 없음".
10340
10351
  // 동시에 서버 프로세스가 cwd 에 `mcp: N` 을 쌓아 남의 프로젝트 통계를 오염시켰다.
10341
- {
10352
+ if (e2eShard.take('D(1.36.104)')) {
10342
10353
  total++;
10343
10354
  let ok = false; const dbg = {};
10344
10355
  const arena = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-us104-'));
@@ -10383,7 +10394,7 @@ total++;
10383
10394
  // ── 1.36.104 (P-0014): 시크릿 인정 기제 — 오경보를 없애되 눈이 멀지는 않는다.
10384
10395
  // 동기: leerness 자기 저장소가 63건을 보고했고 전부 의도적 가짜였다(e2e.js 47 · e2e-core.js 5 · pure-utils 4 · 보고서 7).
10385
10396
  // 매 세션 같은 숫자가 뜨면 진짜 유출이 하나 섞여도 구분되지 않는다. 그래서 대조군 3종을 함께 건다.
10386
- {
10397
+ if (e2eShard.take('E(1.36.104/P-0014)')) {
10387
10398
  total++;
10388
10399
  let ok = false; const dbg = {};
10389
10400
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sb104-'));
@@ -10448,7 +10459,7 @@ total++;
10448
10459
 
10449
10460
  // ── 1.36.104: handoff 가 보호 파일을 지웠다 — 표식 줄의 본문 해시로 '우리가 쓴 그대로' 일 때만 청소한다.
10450
10461
  // 실측 귀속: 명령을 하나씩 단독 실행하니 handoff 였다(이전 라운드엔 외부 검수 탓으로 오귀속하고 있었다).
10451
- {
10462
+ if (e2eShard.take('F(1.36.104)')) {
10452
10463
  total++;
10453
10464
  let ok = false; const dbg = {};
10454
10465
  const arena = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-rem104-'));
@@ -10482,7 +10493,7 @@ total++;
10482
10493
  // ── 1.36.105 (P-0015): 운영 등급 — 등급이 '이름' 이 아니라 '읽히는 양' 을 바꾸는가.
10483
10494
  // 실측 동기: init --minimal 은 디스크 파일만 줄이고 세션 적재는 2,800→2,790 tok(-0.3%) 였다.
10484
10495
  // 대조군 둘이 핵심이다 — 기존 사용자는 무변경이어야 하고, 최소 등급에서도 핵심 3종은 살아 있어야 한다.
10485
- {
10496
+ if (e2eShard.take('G(1.36.105/P-0015)')) {
10486
10497
  total++;
10487
10498
  let ok = false; const dbg = {};
10488
10499
  const arena = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-md105-'));
@@ -10566,7 +10577,7 @@ total++;
10566
10577
  // ── 1.36.105 (P-0009 B, 사용자 승인): 검증 스캐폴딩 토글 3종.
10567
10578
  // 승인 조건이 "경계 계약을 시험으로 고정" 이었다 — 토글이 끄는 것은 **지시문** 뿐이고,
10568
10579
  // 도구가 실제로 실행해 남기는 증거(scan/audit/gate/verify-claim/session close)는 어떤 조합에서도 살아야 한다.
10569
- {
10580
+ if (e2eShard.take('H(1.36.105/P-0009B)')) {
10570
10581
  total++;
10571
10582
  let ok = false; const dbg = {};
10572
10583
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sc105-'));
@@ -10631,7 +10642,7 @@ total++;
10631
10642
  // ── 1.36.106 (T-0090): selftest/doctor 가 임시 디렉토리를 흘렸다 — 실행당 8개/3개, 실측 누적 83,135개.
10632
10643
  // selftest 는 "CI 친화" 로 광고되는 명령이라 커밋마다 쌓인다. 정적으로는 안 보이고 **실행 전후 증가분**으로만 잡힌다.
10633
10644
  // 대조군: 사용자 명령(handoff/audit)은 원래 0이어야 하고, 여기서도 0이어야 한다(회귀 감시).
10634
- {
10645
+ if (e2eShard.take('I(1.36.106/T-0090)')) {
10635
10646
  total++;
10636
10647
  let ok = false; const dbg = {};
10637
10648
  const proj = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-tl106-'));
@@ -10693,7 +10704,7 @@ total++;
10693
10704
  // 손상 스토어 클래스는 이 저장소에서 3회 재발했고(토글 1.36.49 · preview 형상 1.36.28 · 토글 값 1.36.92),
10694
10705
  // 스토어 9종 × 손상 3유형 = 27조합을 실제로 돌려보니 **manifest 만** 3/3 실패였다 —
10695
10706
  // 다른 8종은 fail-closed 규율이 서 있는데 manifest 에는 없었고, 1.36.105 가 거기에 쓰기(mode set)를 추가했다.
10696
- {
10707
+ if (e2eShard.take('J(1.36.107)')) {
10697
10708
  total++;
10698
10709
  let ok = false; const dbg = {};
10699
10710
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mf107-'));
@@ -10757,7 +10768,7 @@ total++;
10757
10768
  // (실측 exit 0 · 무경고). manifest 는 managedOverwrite 목록에 있어 매번 템플릿으로 재생성되는데
10758
10769
  // 템플릿은 사용자 선택을 모른다. installedAt 도 매 재init 마다 갱신돼 이름이 거짓말을 하고 있었다.
10759
10770
  // 고치면서 반대 실패(등급을 바꿀 수 없게 되는 false-STICK)를 만들지 않았는지 함께 단언한다.
10760
- {
10771
+ if (e2eShard.take('K(1.36.107)')) {
10761
10772
  total++;
10762
10773
  let ok = false; const dbg = {};
10763
10774
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mk107-'));
@@ -10833,7 +10844,7 @@ total++;
10833
10844
  //
10834
10845
  // 유실은 가설이 아니라 재현이다: 읽기 직후를 4초 늘린 프로세스 A 와 그 창의 프로세스 B 를 붙이면
10835
10846
  // 락 있는 스토어는 B 가 2.4초 대기 후 둘 다 생존하고, 락 없던 user-requests.json 은 B 의 요청이 통째로 사라졌다.
10836
- {
10847
+ if (e2eShard.take('L(1.36.108/T-0097, 1.36.169/T-0108)')) {
10837
10848
  total++;
10838
10849
  let ok = false; const dbg = {};
10839
10850
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-lock108-'));
@@ -10989,7 +11000,7 @@ total++;
10989
11000
  // Next.js `pages/_app.tsx`, 파이썬 `__init__.py` 처럼 흔한 이름이 전부 여기 걸렸다. 이유 없이 막히면
10990
11001
  // 사용자는 게이트를 끈다 — false-BLOCK 이 false-PASS 보다 나쁘다.
10991
11002
  // 두 방향을 한 블록에서 함께 단언한다. 한쪽만 보면 "전부 거부" 나 "전부 통과" 로 도망갈 수 있다.
10992
- {
11003
+ if (e2eShard.take('M(1.36.109)')) {
10993
11004
  total++;
10994
11005
  let ok = false; const dbg = {};
10995
11006
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-claim109-'));
@@ -11084,7 +11095,7 @@ total++;
11084
11095
  // 구조적 병목은 "산문에 완료 낱말이 있는가" 를 다시 요구한 것이었다 — verify-claim 은 이미 done 행에만
11085
11096
  // 이 검사를 돌린다. 호출부가 아는 사실(claimedDone)을 넘겨 중복 요구를 없앴다.
11086
11097
  // FP 를 먼저 고정했다: 실제 done 이력 139건 발화 0 · 홀드아웃 정직 주장 8/8 무탐지.
11087
- {
11098
+ if (e2eShard.take('N(1.36.110/T-0099)')) {
11088
11099
  total++;
11089
11100
  let ok = false; const dbg = {};
11090
11101
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-honest110-'));
@@ -11155,7 +11166,7 @@ total++;
11155
11166
  // 기준선 실측: 43개 명령 중 31개가 새고 누출 234줄. 이번 라운드에 `commands` 카탈로그를 이중언어화해
11156
11167
  // 234 → 134 로 줄였다. 나머지는 **부채**이고, 이 가드는 그 부채가 **늘지 못하게** 한다.
11157
11168
  // ⚠ baseline 은 목표가 아니라 측정값이다. 줄이는 방향으로만 갱신한다(갚은 뒤에는 반드시 조인다).
11158
- {
11169
+ if (e2eShard.take('O(1.36.111/T-0092)')) {
11159
11170
  total++;
11160
11171
  let ok = false; const dbg = {};
11161
11172
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-i18n111-'));
@@ -11286,7 +11297,7 @@ total++;
11286
11297
  // 최악(이 저장소)은 지침 파일의 78.0%(6,048 tok)이고, minimal 로 낮춰도 적재 7,242 tok 중 83% 가 그것이다.
11287
11298
  // (이 수치는 **출하되는 `_splitPreserved` 로** 다시 잰 값이다 — 분해기를 엄격하게 고친 뒤 재측정했다.)
11288
11299
  // 이 가드는 "재서 보인다" 만 지킨다. 지우는 동작은 여전히 없고, 그게 없다는 것도 아래에서 단언한다.
11289
- {
11300
+ if (e2eShard.take('P(1.36.112)')) {
11290
11301
  total++;
11291
11302
  let ok = false; const dbg = {};
11292
11303
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-carry112-'));
@@ -11516,7 +11527,7 @@ total++;
11516
11527
  // 1.9.402(UR-0108)가 decisions/lessons 의 **md 투영**에 _lineSafe 를 걸었지만, 같은 클래스의
11517
11528
  // 나머지 발화점이 남아 있었다 — plan.md 쓰기(handoff 까지 전파) + 목록 출력 12곳.
11518
11529
  // 가드는 발견 하나가 아니라 **행렬 전체**를 고정한다. 한 표면만 막으면 다음 헌트에서 옆 표면이 나온다.
11519
- {
11530
+ if (e2eShard.take('Q(1.36.113)')) {
11520
11531
  total++;
11521
11532
  let ok = false; const dbg = {};
11522
11533
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-inj113-'));
@@ -11648,7 +11659,7 @@ total++;
11648
11659
  // ⚠ 이 라운드가 만든 회귀도 여기서 잡았다: 손상 시 저장은 막혔는데 명령이 `✓ 추가` 를 찍고 exit 0 이었다.
11649
11660
  // 조용한 무동작 + 성공 표시는 조용한 유실보다 나쁘다 — 사용자는 저장됐다고 믿는다. 그래서 **세 가지를 함께** 단언한다:
11650
11661
  // 안 쓴다 · 성공이라 말하지 않는다 · 정상 스토어에서는 오차단하지 않는다.
11651
- {
11662
+ if (e2eShard.take('R(1.36.114)')) {
11652
11663
  total++;
11653
11664
  let ok = false; const dbg = {};
11654
11665
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-neg114-'));
@@ -11827,7 +11838,7 @@ total++;
11827
11838
  // ③ `init` 이 `.mcp.json` 을 만들지 않았다 — `adapter list` 는 claude 에 "[+.mcp.json]" 이라 광고하는데
11828
11839
  // 문서화된 진입점인 init 은 그 배선을 빼먹어, init 만 한 사용자의 Claude Code 는 도구를 하나도 못 봤다.
11829
11840
  // 이 가드는 "설치하면 도구가 leerness 를 쓰게 된다" 는 **제품의 기본 약속**을 계약으로 고정한다.
11830
- {
11841
+ if (e2eShard.take('S(1.36.115)')) {
11831
11842
  total++;
11832
11843
  let ok = false; const dbg = {};
11833
11844
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-wire115-'));
@@ -12205,7 +12216,7 @@ total++;
12205
12216
  // ⚠ 이 기능의 성패는 **오탐**에 달렸다. 잘 만든 프로젝트는 CI 에 이름을 나열하지 않고 package.json 을
12206
12217
  // 동적으로 열거한다 — 그걸 못 알아보면 가장 모범적인 저장소에서 수십 건이 뜨고 사용자는 경고를 꺼 버린다.
12207
12218
  // 실제로 초안이 그 실측 프로젝트에서 **85건 오탐**을 냈고(근접 창 가정 때문), 그래서 아래 세 대조군을 모두 건다.
12208
- {
12219
+ if (e2eShard.take('T(1.36.116)')) {
12209
12220
  total++;
12210
12221
  let ok = false; const dbg = {};
12211
12222
  try {
@@ -12847,8 +12858,8 @@ total++;
12847
12858
  // 메모화로 1회로 줄였다. 그런데 "안 돌렸다" 와 "한 번 돌리고 결과를 나눠 줬다" 는 겉보기가 같다 —
12848
12859
  // 그래서 **실패하는 스위트**를 판별 케이스로 쓴다: 전파되면 N개 주장 **전부** 실패하고, 끊기면 첫 주장만 실패한다.
12849
12860
  // 대조군(통과 스위트)도 함께 둔다 — 메모가 없는 실패를 지어내지 않는지.
12850
- total++;
12851
- {
12861
+ if (e2eShard.take('U(1.36.125)')) {
12862
+ total++;
12852
12863
  let ok = false; const bad = []; const dbg = {};
12853
12864
  const _dirs = [];
12854
12865
  const N = 3;
@@ -12899,8 +12910,8 @@ total++;
12899
12910
  // 1.36.126 → 1.36.162 (T-0107): `.leerness/` 가 공식 canonical 이 된 뒤의
12900
12911
  // 호환 계약. 과거에는 대체 디렉터리를 거부했지만, 이제는 fresh init·idempotency·
12901
12912
  // marker가 권위를 낮추지 않는 dual-live fail-closed·부모 자산 해석을 검증한다.
12902
- total++;
12903
- {
12913
+ if (e2eShard.take('V(1.36.126/T-0107)')) {
12914
+ total++;
12904
12915
  let ok = false; const bad = []; const dbg = {};
12905
12916
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ws126-'));
12906
12917
  const envW = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -13038,8 +13049,8 @@ total++;
13038
13049
  // CI 가 `bin/leerness.js` 를 실행하므로 그 파일이 러너로 잡히고, 그 안의 selftest 단언 문자열
13039
13050
  // (`'test:core'`·`e2e-core.js`)이 호출로 읽혀 leerness 자신의 진짜 고아가 0건으로 보고됐다.
13040
13051
  // ① audit 이 그 가림을 **사용자에게 말하는가** ② 대조군은 조용한가 ③ 그리고 우리 자신이 실제로 배선했는가.
13041
- total++;
13042
- {
13052
+ if (e2eShard.take('W(1.36.127/T-0105)')) {
13053
+ total++;
13043
13054
  let ok = false; const bad = []; const dbg = {};
13044
13055
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-mask127-'));
13045
13056
  const envM = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -13148,8 +13159,8 @@ total++;
13148
13159
  // 1.36.128 (T-0104 실측): `adapter <tool> [+.mcp.json]` 은 **외부 제품이 그 파일을 읽는다**는 주장이다.
13149
13160
  // 이 머신의 실제 CLI 로 재 보니 codex 는 읽지 않았고(`codex mcp get` → not found) opencode 는 읽었다(connected).
13150
13161
  // 그래서 안 읽는 도구에는 파일을 **쓰지 않고** 되는 명령을 안내한다. 그 계약을 CLI 행위로 고정한다.
13151
- total++;
13152
- {
13162
+ if (e2eShard.take('X(1.36.128/T-0104)')) {
13163
+ total++;
13153
13164
  let ok = false; const bad = []; const dbg = {};
13154
13165
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-adp128-'));
13155
13166
  const envA = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -13229,8 +13240,8 @@ total++;
13229
13240
  // 설계 경계는 전부 실측에서 나왔다: PID 라이브니스는 양방향으로 틀리고(5초에 재사용 7건, 죽은 pid 5.2%가 ALIVE),
13230
13241
  // `leerness selftest` 1회가 leerness 프로세스 129개를 띄우므로 "모든 호출마다 등록" 은 불가능하다.
13231
13242
  // 그래서 쓰기 지점은 handoff · session close 둘뿐이고, 판정("활성")은 아예 하지 않는다.
13232
- total++;
13233
- {
13243
+ if (e2eShard.take('Y(1.36.129/P-0016 P1)')) {
13244
+ total++;
13234
13245
  let ok = false; const bad = []; const dbg = {};
13235
13246
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sess129-'));
13236
13247
  // ⚠ 이 e2e 는 **살아있는 Claude Code 세션 안에서** 돈다 — 주변 env 를 비우지 않으면
@@ -13447,8 +13458,8 @@ total++;
13447
13458
  // → 이제 **별도 프로세스**가 해제 시각을 파일로 남기고, 대상 파일 **mtime 과 비교**한다.
13448
13459
  // "잠깐 기다렸다 무락으로 쓰기" 도 mtime < 해제시각 이라 걸린다.
13449
13460
  // · 사용자 글 보존을 `includes` 로만 봐서 **표 뒤로 재배치**해도 통과했다 → 이제 **위치**를 단언한다.
13450
- total++;
13451
- {
13461
+ if (e2eShard.take('Z(1.36.131/P-0016 P2)')) {
13462
+ total++;
13452
13463
  let ok = false; const bad = []; const dbg = {};
13453
13464
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-p2-131-'));
13454
13465
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -13609,8 +13620,8 @@ total++;
13609
13620
  // ② 세션 신호가 4표면 중 1곳에만 닿았고, 못 가는 3곳에 유일한 강제 경로(훅)·MCP 경로·기계 경로가 들어 있었다.
13610
13621
  // ③ 억제된 세션은 "남이 N건 있다" 는 듣지만 "나는 안 보인다" 는 못 들었다(비대칭이 조용했다).
13611
13622
  // 그리고 그 밑에서 락 자체가 새고 있었다(아래 ①).
13612
- total++;
13613
- {
13623
+ if (e2eShard.take('AA(1.36.132/UR-0069)')) {
13624
+ total++;
13614
13625
  let ok = false; const bad = []; const dbg = {};
13615
13626
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-132-'));
13616
13627
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -13895,8 +13906,8 @@ total++;
13895
13906
  // 자동유도 96.9% · 명시선언 63.1% · 혼합(채택률 1.0) 94.0% 로 전부 40% 초과였고, 유일한 생존 구성은
13896
13907
  // **충돌이 실제로 나는 파일(bin/leerness.js, 커밋의 68.8%)을 음소거해서** 조용해진 것이었다(무출력은 통과가 아니다).
13897
13908
  // 대신 그 정찰이 실측한 **기반 결함**을 고친다 — 전부 P3-A 없이도 이미 출하돼 있던 동작이다.
13898
- total++;
13899
- {
13909
+ if (e2eShard.take('AB(1.36.133/T-0109)')) {
13910
+ total++;
13900
13911
  let ok = false; const bad = []; const dbg = {};
13901
13912
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-133-'));
13902
13913
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -14126,8 +14137,8 @@ total++;
14126
14137
  // 그리고 `status --json` 은 git 아님/unborn/정상/HEAD손상/gitdir깨짐 **5상태에서 payload 가 동일**해
14127
14138
  // '설치 안 됨' 과 '설치할 데가 없음' 이 구분되지 않았다.
14128
14139
  // 원인은 근거가 셋이었다는 것이다: install=`.git` **디렉토리 존재**, audit=`git log` 실행 결과, status=아무것도 안 봄.
14129
- total++;
14130
- {
14140
+ if (e2eShard.take('AC(1.36.134)')) {
14141
+ total++;
14131
14142
  let ok = false; const bad = []; const dbg = {};
14132
14143
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-134-'));
14133
14144
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -14578,14 +14589,14 @@ total++;
14578
14589
  if (!ok) failed++;
14579
14590
  }
14580
14591
 
14581
- total++;
14582
14592
  // AD(1.36.140) — **초크포인트는 하나여야 한다.**
14583
14593
  // 1.36.135/139 는 `bin/leerness.js` 의 git 초크포인트에만 환경 세척을 넣었고,
14584
14594
  // `lib/drift.js` · `lib/session-close.js` 는 같은 이름의 복제본을 갖고 있었다(세척 없음).
14585
14595
  // 기존 가드는 "인라인 배열 형태의 직접 호출 0건" 만 셌다 — 세 복제본이 전부 변수 형태라 **모양은 통과**했다.
14586
14596
  // 가드가 밟지 않는 경로는 가드가 지키지 못한다. 그래서 여기서는 **행위로** 잰다:
14587
14597
  // 상속된 GIT_DIR 아래에서 남의 저장소 브랜치가 지워지는가(실측: 고치기 전 60 → 10, 50건 삭제).
14588
- {
14598
+ if (e2eShard.take('AD(1.36.140)')) {
14599
+ total++;
14589
14600
  let ok = false; const bad = []; const dbg = {};
14590
14601
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-140-'));
14591
14602
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -14693,14 +14704,14 @@ total++;
14693
14704
 
14694
14705
 
14695
14706
 
14696
- total++;
14697
14707
  // AE(1.36.140) — 재검수가 지적하고 **내가 재현한** 것들을 고정한다.
14698
14708
  // 전부 "고치려고 넣은 코드가 만든 다음 결함" 이다:
14699
14709
  // · 복합 키 예산(1.36.139) → `date` 안의 `|` 로 두 항목이 같은 키가 되어 **무관 항목 삭제 + archive 불일치**
14700
14710
  // · 롤백(1.36.134~139) → 정의가 실패 지점보다 뒤라 EPERM 이 최상위로 튀며 **체인 백업이 덮인 채 남음**
14701
14711
  // · archive 를 락 밖에서 씀 → 동시 drop 둘 다 성공 보고 + 이중 archive → 복원이 중복 생성
14702
14712
  // 판정값은 모두 **실측 전/후 비교**다(문자열 존재가 아니라 무엇이 남았는지).
14703
- {
14713
+ if (e2eShard.take('AE(1.36.140)')) {
14714
+ total++;
14704
14715
  let ok = false; const bad = []; const dbg = {};
14705
14716
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-140b-'));
14706
14717
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -14887,7 +14898,6 @@ total++;
14887
14898
  if (!ok) failed++;
14888
14899
  }
14889
14900
 
14890
- total++;
14891
14901
  // AF(1.36.142/T-0116) — **증거는 일한 세션에 붙어야 한다.**
14892
14902
  // `.leerness/state.json` 의 `currentRunId` 는 전역 슬롯 하나였다. 두 세션이 같은 프로젝트에서 일하면
14893
14903
  // 뒤에 `state start` 한 쪽이 앞사람 슬롯을 덮어썼고, 실측으로:
@@ -14895,7 +14905,8 @@ total++;
14895
14905
  // · 일하지 않은 B 가 `completion_claim_allowed: true` 를 받았고
14896
14906
  // · A 는 `state show` 로 **자기 run 을 볼 수조차 없었다**.
14897
14907
  // 락 문제가 아니다(직렬로 돌려도 재현된다) — 주소가 없는 것이 문제다.
14898
- {
14908
+ if (e2eShard.take('AF(1.36.142/T-0116)')) {
14909
+ total++;
14899
14910
  let ok = false; const bad = []; const dbg = {};
14900
14911
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-142-'));
14901
14912
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15009,14 +15020,14 @@ total++;
15009
15020
  if (!ok) failed++;
15010
15021
  }
15011
15022
 
15012
- total++;
15013
15023
  // AG(1.36.143) — **없는 `--path` 는 아무것도 만들지 않는다** + 공유 판정은 추정하지 않는다.
15014
15024
  // 검수는 `provider add` · `team add` · `creds register` 3건을 지목했다. 이름을 더 넣는 대신
15015
15025
  // 모든 명령에 없는 `--path` 를 주고 전수로 재니 **33건**이 그 경로를 만들었다 —
15016
15026
  // 다수는 **성공이라 말하면서**(`provider add` · `team add` · `skill learn` · `glossary` · `wakeup-interval`),
15017
15027
  // 그중엔 직전 라운드에 내가 넣은 `state start` 도 있었다. 개별 등록은 이미 세 번째였다.
15018
15028
  // 그래서 이름 목록이 아니라 **성질**로 막고, 여기서도 이름이 아니라 **열거**로 잰다.
15019
- {
15029
+ if (e2eShard.take('AG(1.36.143)')) {
15030
+ total++;
15020
15031
  let ok = false; const bad = []; const dbg = {};
15021
15032
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-143-'));
15022
15033
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15176,12 +15187,12 @@ total++;
15176
15187
  if (!ok) failed++;
15177
15188
  }
15178
15189
 
15179
- total++;
15180
15190
  // AH(1.36.144/T-0117) — **`--dry-run` 은 아무것도 쓰지 않는다** + 재검수 P1/P2.
15181
15191
  // `--dry-run` 은 등록된 전역 플래그인데 많은 명령이 그냥 무시하고 썼다. 378 호출을 전후 트리 비교로 재니
15182
15192
  // 183건이 썼고 그중 28건이 **사용자 데이터**를 바꿨다(`session close` 가 실제 마감을 수행, `task/decision/rule/plan add`
15183
15193
  // 가 실제로 추가). 명령마다 분기를 심으면 16개의 새 실수 자리가 생기므로 **쓰기 시점 하나**에서 막는다.
15184
- {
15194
+ if (e2eShard.take('AH(1.36.144/T-0117)')) {
15195
+ total++;
15185
15196
  let ok = false; const bad = []; const dbg = {};
15186
15197
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-144-'));
15187
15198
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15332,12 +15343,12 @@ total++;
15332
15343
  if (!ok) failed++;
15333
15344
  }
15334
15345
 
15335
- total++;
15336
15346
  // AI(1.36.145) — **MCP 표면에도 세션 주소가 있어야 한다.**
15337
15347
  // T-0116 을 CLI 에서만 고쳤다. MCP `state` 도구에는 주소 입력이 없어 한 서버의 모든 호출이
15338
15348
  // 같은(=무)주소로 접혀 전역 슬롯 오귀속이 그대로 남아 있었다 — 실측: A 의 증거가 B 의 run 에 붙고
15339
15349
  // 일하지 않은 B 가 `completion_claim_allowed: true` 를 받았다. 가드는 자기가 밟는 경로만 지킨다.
15340
- {
15350
+ if (e2eShard.take('AI(1.36.145)')) {
15351
+ total++;
15341
15352
  let ok = false; const bad = []; const dbg = {};
15342
15353
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-145-'));
15343
15354
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15427,7 +15438,6 @@ total++;
15427
15438
  if (!ok) failed++;
15428
15439
  }
15429
15440
 
15430
- total++;
15431
15441
  // AJ(1.36.146) — dry-run 가드가 **쓰기만** 보고 있었다.
15432
15442
  // 1.36.144 의 가드는 `writeUtf8`/`append`/`mkdirp` 세 함수만 봤다. io 를 우회하는 `fs.*` 변경 호출은
15433
15443
  // 정적으로 **330건**(unlinkSync 15 · rmSync 85 · writeFileSync 125 …) 이었고, 실측으로:
@@ -15436,7 +15446,8 @@ total++;
15436
15446
  // · 면제 정규식이 `cache` 를 경로 어디서나 매칭해 `…/cache/proj/` 프로젝트는 **가드가 통째로 꺼졌다**
15437
15447
  // · MCP 의 빈 `path` 가 서버 root 로 대체돼 엉뚱한 저장소에 썼다
15438
15448
  // 330곳을 배선하면 330개의 새 실수 자리가 생긴다 — 프로세스 진입점에서 한 번 막고, git 은 초크포인트에서 막는다.
15439
- {
15449
+ if (e2eShard.take('AJ(1.36.146)')) {
15450
+ total++;
15440
15451
  let ok = false; const bad = []; const dbg = {};
15441
15452
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-146-'));
15442
15453
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15547,13 +15558,13 @@ total++;
15547
15558
  if (!ok) failed++;
15548
15559
  }
15549
15560
 
15550
- total++;
15551
15561
  // AK(1.36.147) — 소유권 코드가 만든 세 결함. 전부 **내가 T-0116 을 고치며 넣은 것**이다.
15552
15562
  // ① 인수는 했는데 **소유를 저장하지 않아** 두 세션이 같은 legacy run 을 각자 인수했다
15553
15563
  // (실측: 한 run 에 A.js 와 B.js 가 함께 들어갔다 — T-0116 이 잡으려던 바로 그 증거 혼합).
15554
15564
  // ② 같은 세션 키로 두 번 `start` 하면 둘 다 성공하고 마지막만 소유해 **첫 run 이 증거 없이 고아**가 됐다.
15555
15565
  // ③ 남의 세션 키 가림이 최상위 필드에만 걸려 **중첩 `state.runsBySession`** 으로 원본이 그대로 샜다.
15556
- {
15566
+ if (e2eShard.take('AK(1.36.147)')) {
15567
+ total++;
15557
15568
  let ok = false; const bad = []; const dbg = {};
15558
15569
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-147-'));
15559
15570
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15629,7 +15640,6 @@ total++;
15629
15640
  if (!ok) failed++;
15630
15641
  }
15631
15642
 
15632
- total++;
15633
15643
  // AL(1.36.148) — 앞 라운드 수정들이 **다른 조건에서 무력화**되던 자리들.
15634
15644
  // ① `keySource` 가 child 억제를 **명시 주소보다 먼저** 검사해, MCP 서버가
15635
15645
  // `CLAUDE_CODE_CHILD_SESSION=1` 을 상속하면 1.36.145 의 주소 전달이 통째로 죽었다.
@@ -15637,7 +15647,8 @@ total++;
15637
15647
  // ② run id 를 "빈 문자열만 아니면" 으로 봐서 `../../evil` 이 소유 맵에 들어가면 `.leerness` 밖에 썼다.
15638
15648
  // ③ 중앙 `--path` 가드가 존재성만 봐서 **일반 파일**이 프로젝트 root 로 통과했다.
15639
15649
  // ④ dry-run 가드는 파일·git 만 봤다 — `team deploy --yes --dry-run` 이 배포 subprocess 를 실제 실행했다.
15640
- {
15650
+ if (e2eShard.take('AL(1.36.148)')) {
15651
+ total++;
15641
15652
  let ok = false; const bad = []; const dbg = {};
15642
15653
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-148-'));
15643
15654
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15755,11 +15766,11 @@ total++;
15755
15766
  if (!ok) failed++;
15756
15767
  }
15757
15768
 
15758
- total++;
15759
15769
  // AM(1.36.149) — 1.36.148 의 run-id 경로 차단은 소유 맵만 덮었다.
15760
15770
  // legacy `currentRunId` 와 디스크 run record 의 `run_id` 도 같은 파일 경로 병목을 탄다.
15761
15771
  // 둘 중 하나가 경로형이면 state record/handoff 가 프로젝트 밖에 쓰기 전에 손상으로 멈춰야 한다.
15762
- {
15772
+ if (e2eShard.take('AM(1.36.149)')) {
15773
+ total++;
15763
15774
  let ok = false; const bad = []; const dbg = {};
15764
15775
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-149-'));
15765
15776
  const envP = Object.assign({}, process.env, { TMPDIR: sb, TEMP: sb, TMP: sb, LEERNESS_OFFLINE: '1', LEERNESS_NO_PROMPT: '1' });
@@ -15813,11 +15824,11 @@ total++;
15813
15824
  if (!ok) failed++;
15814
15825
  }
15815
15826
 
15816
- total++;
15817
15827
  // AN(1.36.150/T-0120) — `GIT_CONFIG_NOSYSTEM`은 저장소 위치 주입이 아니라 호출자가 시스템 설정을
15818
15828
  // 숨기겠다는 의미다. 설치기만 이를 지우면 system core.hooksPath에 훅을 쓰지만, 같은 셸의 실제 git commit은
15819
15829
  // 기본 .git/hooks를 보아 오래된 handoff도 통과한다. 설치 경로와 실제 커밋 경로가 같은 환경에서 일치해야 한다.
15820
- {
15830
+ if (e2eShard.take('AN(1.36.150/T-0120)')) {
15831
+ total++;
15821
15832
  let ok = false; const bad = []; const dbg = {};
15822
15833
  const sb = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-150-'));
15823
15834
  const stripGitTargetEnv = (source) => {
@@ -15878,12 +15889,12 @@ total++;
15878
15889
  if (!ok) failed++;
15879
15890
  }
15880
15891
 
15881
- total++;
15882
15892
  // AO(1.36.152/T-0115, 1.36.169/T-0108 확장) — write 시점의 락 관측만으로는 "읽은 사본도 락 안인가"를 증명할 수 없다.
15883
15893
  // 첫 프로세스의 최종 rename을 강제로 멈추고 두 번째 쓰기를 붙여, 서로 다른 RMW 11쌍이 모두 생존하는지 확인한다.
15884
15894
  // T-0343: plus 19 pairs where the second command decided a write, a skip or an error from a file the first one was
15885
15895
  // publishing (on POSIX the path is missing for that moment); 30 in all.
15886
- {
15896
+ if (e2eShard.take('AO(1.36.152/T-0115, 1.36.169/T-0108, T-0343)')) {
15897
+ total++;
15887
15898
  let ok = false; const dbg = {};
15888
15899
  try {
15889
15900
  const race = cp.spawnSync(process.execPath, [path.join(__dirname, 'e2e-concurrency.js'), CLI], {
@@ -15916,7 +15927,7 @@ setImmediate(() => {
15916
15927
  } catch (error) {
15917
15928
  runtimeDep0190.push(`warning redirect gate read failed: ${error.message}`);
15918
15929
  }
15919
- total++;
15930
+ total++; // e2e-shard: run-wide check
15920
15931
  if (runtimeDep0190.length === 0) {
15921
15932
  console.log('✓ AP(1.36.162/T-0139) 전체 E2E 부모+동기·ignore·async 자식 런타임 DEP0190 0건');
15922
15933
  } else {
@@ -15925,7 +15936,7 @@ setImmediate(() => {
15925
15936
  process.stderr.write(runtimeDep0190.join('\n---\n') + '\n');
15926
15937
  }
15927
15938
  // T-0315: 테스트는 패키지 저장소 자신의 상태를 바꾸지 않는다 — gitignore 된 캐시·원장까지 전부 대조한다.
15928
- total++;
15939
+ total++; // e2e-shard: run-wide check
15929
15940
  const _pkgChanged = repoGuard.diff(_pkgLeernessBefore, repoGuard.snapshot(_pkgLeerness));
15930
15941
  if (_e2eHomeInit.status === 0 && _pkgChanged.length === 0) {
15931
15942
  console.log(`✓ T-0315 e2e 가 패키지 저장소 .leerness 를 바꾸지 않음 (항목 ${_pkgLeernessBefore.size}개 대조 · cwd 없는 CLI 호출은 격리 홈 프로젝트에서 실행)`);
@@ -15934,6 +15945,16 @@ setImmediate(() => {
15934
15945
  console.log(`✗ T-0315 e2e 격리 실패 — 격리 홈 init exit=${_e2eHomeInit.status}, 패키지 .leerness 변경 ${_pkgChanged.length}건: ${_pkgChanged.slice(0, 20).join(', ')}${_pkgChanged.length > 20 ? ' …' : ''}`
15935
15946
  + ' (실행 중 이 저장소에서 leerness 를 직접 실행했다면 그 쓰기도 여기에 잡힌다)');
15936
15947
  }
15937
- console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
15948
+ // T-0359: every case of this file was reached exactly once, here or in its own shard, and none is unguarded.
15949
+ total++; // e2e-shard: run-wide check
15950
+ const _shard = e2eShard.finish();
15951
+ const _shardName = _shard.spec ? `샤드 ${_shard.spec.index}/${_shard.spec.count}` : '샤드 없음(전체)';
15952
+ if (_shard.ok) {
15953
+ console.log(`✓ T-0359 e2e ${_shardName}: 케이스 ${_shard.ran}/${_shard.total}개 실행 · 가드 ${_shard.total}개가 각각 한 번씩 도달 · 가드 없는 최상위 블록 0`);
15954
+ } else {
15955
+ failed++;
15956
+ console.log(`✗ T-0359 e2e 샤드 가드 이상 ${JSON.stringify({ problems: _shard.problems.slice(0, 10), missing: _shard.missing.slice(0, 10), repeated: _shard.repeated.slice(0, 10), unknown: _shard.unknown.slice(0, 10) })}`);
15957
+ }
15958
+ console.log(`\nE2E result: ${total - failed}/${total} passed${_shard.spec ? ` · shard ${_shard.spec.index}/${_shard.spec.count} (${_shard.ran}/${_shard.total} cases)` : ''} · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
15938
15959
  if (failed > 0) process.exitCode = 1;
15939
15960
  });