leerness 1.36.175 → 1.36.176

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.36.176 — 2026-08-29
4
+
5
+ - T-0089: agents bench가 활성화되지 않은 provider의 버전 명령까지 실행하던 환경 의존성을 제거했습니다. bench 후보를 opt-in provider로 먼저 제한하고, E2E는 레지스트리의 10개 플래그를 모두 비활성화한 뒤 PATH 선두 실행 마커가 생성되지 않는지 검증합니다. 수정 후 동일 Windows Node 26 환경 직접 프로브 3회는 394/362/342ms(최대 394ms)였으며, 기존 15초 제한은 최댓값 대비 약 38.1배 여유라 불필요한 상향을 하지 않습니다.
6
+
3
7
  ## 1.36.175 — 2026-08-29
4
8
 
5
9
  - T-0095 false-claim 감사를 199개 회귀 프로브로 고정했습니다. round-history와 release cadence는 CLI·프로젝트 하네스·저장소 태그 버전 및 하네스 판독 상태의 출처를 분리하고 기존 currentVersion 별칭에 leerness_cli 범위를 명시합니다. observation-only로 명시 분류한 `about`·`identity`·`status`·`commands`·`install-safety`·`capabilities`·`round-history`·`milestones`·`pulse`·`release cadence`와 MCP 서버 시작은 레거시 `.harness/`를 마이그레이션하거나 사용량 텔레메트리를 쓰지 않습니다. 읽기 전용 MCP 도구도 서버의 우연한 cwd와 명시 대상을 보존합니다. 이 계약은 상위 프로세스의 `LEERNESS_INTERNAL`까지 명시적으로 제거한 격리 환경에서 canonical 대상의 실제 usage 파일로 검증하며, 쓰기 MCP 호출은 대상 프로젝트에만 계속 귀속됩니다. `status`는 선택된 `.harness/` 또는 `.leerness/`를 한 번만 해석해 실제 버전·누락 경로·`workspaceDir`을 보고합니다. `.harness/`·`.leerness/` 충돌이나 잘못된 workspace override는 `unreadable` 성공으로 삼키지 않고 정확한 `workspace_dir_*` 오류로 종료하며, pulse/handoff 집계는 workspace 출처 오류를 구조화해 노출하면서 독립적으로 측정한 Git 라운드 수를 보존합니다. Git 누락·spawn 예외·timeout·signal·nonzero exit이면 round-history·milestones·release cadence는 `git_history_unavailable`로 실패하고 pulse/handoff/health/session-close는 `roundCount:null`, milestone의 `totalRounds/reachedCount/reached/avgRoundsPerDay:null`, `recentChanges:null`과 `recentChangesState/recentChangesError`를 보존해 R0/빈 이력으로 위장하지 않습니다. Git 측정 성공 시에는 기존 `recentChanges` 배열 계약을 그대로 유지하고, `latestTags[].date`의 전체 타임스탬프 계약도 보존하면서 별도 `dateOnly`를 제공합니다. 예외·timeout·signal·nonzero 네 시나리오는 일곱 공개 표면 전부에서 각각 실행합니다. cadence는 `--path > positional > cwd` 단일 우선순위를 검증과 실행에 함께 쓰고, 태그 간격이 없으면 healthy가 아니라 `insufficient-data`로 판정을 보류합니다. E2E/적대 프로브의 Git 픽스처도 공용 `gitSpawn`을 사용해 상위 셸의 외부 저장소·index·일회성 config 주입을 제거하며, persistent global/system Git config도 빈 임시 파일로 격리해 외부 hooksPath가 픽스처에서 실행되지 않게 합니다.
package/README.md CHANGED
@@ -138,7 +138,7 @@ MIT
138
138
  <!-- leerness:project-readme:start -->
139
139
  ## Leerness Project Harness
140
140
 
141
- 이 프로젝트는 Leerness v1.36.175 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
141
+ 이 프로젝트는 Leerness v1.36.176 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
142
142
 
143
143
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
144
144
 
@@ -192,7 +192,7 @@ leerness memory restore decision <date|title>
192
192
 
193
193
  ### MCP server (외부 AI 통합)
194
194
 
195
- Leerness v1.36.175는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **89개 도구**를 노출:
195
+ Leerness v1.36.176는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **89개 도구**를 노출:
196
196
 
197
197
  ```jsonc
198
198
  // 카테고리별
@@ -213,7 +213,7 @@ Leerness v1.36.175는 stdio JSON-RPC MCP server를 내장합니다 — Claude Co
213
213
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
214
214
  1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
215
215
 
216
- 현재 누적: **v1.9.x → 1.36.175 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
216
+ 현재 누적: **v1.9.x → 1.36.176 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
217
217
 
218
218
  ### 성능 가이드
219
219
 
@@ -251,5 +251,5 @@ leerness release pack --close --auto-main-push
251
251
  - `.leerness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
252
252
  - `.leerness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
253
253
 
254
- Last synced by Leerness v1.36.175: 2026-08-29
254
+ Last synced by Leerness v1.36.176: 2026-08-29
255
255
  <!-- leerness:project-readme:end -->
package/bin/leerness.js CHANGED
@@ -48,7 +48,7 @@ const {
48
48
  migrateLegacyWorkspace,
49
49
  } = require('../lib/workspace-dir');
50
50
 
51
- const VERSION = '1.36.175';
51
+ const VERSION = '1.36.176';
52
52
 
53
53
  // MCP lifecycle 주소 표식은 현재 CLI 호출 한 번에만 유효하다. CLI bootstrap에서 즉시 env에서
54
54
  // 떼어 두어 `--no-record`/hook처럼 presence 기록 함수에 도달하지 않는 경로도 후속 child에 유출하지 않는다.
package/lib/agents.js CHANGED
@@ -401,7 +401,11 @@ function agentsCmd(root, sub, args = [], deps = {}) {
401
401
  if (!task) { fail('bench "<task>" 필요'); return process.exit(1); }
402
402
  const timeoutS = parseInt(arg('--timeout', '60'), 10);
403
403
  const writeMode = has('--write');
404
- const ready = EXTERNAL_AGENTS.map(a => ({ agent: a, status: _checkAgent(a) }))
404
+ // Disabled providers are not bench candidates. Filter before _checkAgent so a
405
+ // bench that has no opted-in provider cannot execute installed third-party
406
+ // CLIs merely to discover that they are disabled.
407
+ const ready = EXTERNAL_AGENTS.filter(a => process.env[a.envFlag] === '1')
408
+ .map(a => ({ agent: a, status: _checkAgent(a) }))
405
409
  .filter(x => x.status.status === 'ready');
406
410
  if (!ready.length) {
407
411
  fail('ready CLI 없음 — leerness setup-agents 또는 .env에 LEERNESS_ENABLE_X=1 설정 필요');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.175",
3
+ "version": "1.36.176",
4
4
  "description": "The AI-coding operations layer that makes \"done\" require evidence — persistent memory, evidence-gated completion checks, and clean handoffs for any AI agent (Claude Code, Codex, Cursor). State lives as plain files in your repo. CLI + MCP, 0 runtime dependencies.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -7,6 +7,7 @@ const path = require('path');
7
7
  const cp = require('child_process');
8
8
  const { spawnNpmSync } = require('../lib/npm-process');
9
9
  const { gitSpawn } = require('../lib/git');
10
+ const { EXTERNAL_AGENTS } = require('../lib/agent-registry');
10
11
  const { withRedirectWarnings } = require('./runtime-warning-gate');
11
12
 
12
13
  // 1.9.12: e2e 안정성을 위해 자식 프로세스의 npm 호출 차단 (hang 방지)
@@ -1912,10 +1913,20 @@ total++;
1912
1913
  total++;
1913
1914
  {
1914
1915
  // bench 명령: ready CLI 없을 때 거부
1915
- const env = { ...process.env, LEERNESS_ENABLE_CLAUDE: '0', LEERNESS_ENABLE_CODEX: '0', LEERNESS_ENABLE_AGY: '0', LEERNESS_ENABLE_COPILOT: '0' };
1916
+ // 빌트인 provider가 늘어나도 상속 환경에서 실제 agent가 실행되지 않도록 레지스트리 전체를 끈다.
1917
+ // PATH 선두의 실행 마커는 disabled provider가 version probe조차 받지 않는지를 검증한다.
1918
+ const fakeBin = path.join(tmp, 'bench-disabled-bin');
1919
+ const marker = path.join(tmp, 'bench-disabled-provider-ran.txt');
1920
+ fs.mkdirSync(fakeBin, { recursive: true });
1921
+ writePortableCliShim(fakeBin, 'codex', `require('fs').writeFileSync(process.env.LEERNESS_E2E_MARKER, 'ran'); console.log('codex fixture');`);
1922
+ const env = { ...process.env, ...Object.fromEntries(EXTERNAL_AGENTS.map(a => [a.envFlag, '0'])) };
1923
+ const pathKey = Object.keys(env).find(k => k.toUpperCase() === 'PATH') || 'PATH';
1924
+ env[pathKey] = `${fakeBin}${path.delimiter}${env[pathKey] || ''}`;
1925
+ env.LEERNESS_E2E_MARKER = marker;
1926
+ // 1.36.176/T-0089: disabled provider를 probe 전 제거하므로 15s 안에서 기능과 무실행 계약을 함께 검사한다.
1916
1927
  const r = cp.spawnSync(process.execPath, [CLI, 'agents', 'bench', 'test'], { encoding: 'utf8', timeout: 15000, env });
1917
- const ok = r.status !== 0 && /ready CLI 없음/.test(r.stdout);
1918
- console.log(ok ? '✓ B(1.9.36) agents bench: ready 없을 때 거부' : `✗ bench 거부 실패`);
1928
+ const ok = r.status !== 0 && /ready CLI 없음/.test(r.stdout) && !fs.existsSync(marker);
1929
+ console.log(ok ? '✓ B(1.9.36) agents bench: ready 없을 때 거부 + disabled CLI 무실행' : `✗ bench 거부/격리 실패`);
1919
1930
  if (!ok) { failed++; console.log(r.stdout.slice(0, 300)); }
1920
1931
  }
1921
1932
 
@@ -439,6 +439,8 @@ try {
439
439
  ' const rows = [];',
440
440
  ' for (const target of EXTERNAL_AGENTS) {',
441
441
  ' const calls = [];',
442
+ ' const checked = [];',
443
+ " for (const agent of EXTERNAL_AGENTS) process.env[agent.envFlag] = agent.id === target.id ? '1' : '0';",
442
444
  ' const fakeSpawn = (file, args, options) => {',
443
445
  ' calls.push({ file, args, stdin: options && options.stdio && options.stdio[0] });',
444
446
  ' const child = new EventEmitter();',
@@ -448,10 +450,10 @@ try {
448
450
  ' };',
449
451
  " const arg = (_name, fallback) => fallback; const has = () => false;",
450
452
  " const results = await agentsCmd(process.cwd(), 'bench', ['matrix task'], {",
451
- " has, arg, _loadEnvFile: () => {}, _checkAgent: agent => ({ status: agent.id === target.id ? 'ready' : 'disabled' }),",
453
+ " has, arg, _loadEnvFile: () => {}, _checkAgent: agent => { checked.push(agent.id); return { status: agent.id === target.id ? 'ready' : 'disabled' }; },",
452
454
  ' _spawnPortable: fakeSpawn,',
453
455
  ' });',
454
- ' rows.push({ id: target.id, calls, results });',
456
+ ' rows.push({ id: target.id, checked, calls, results });',
455
457
  ' }',
456
458
  ' process.stdout.write(JSON.stringify(rows));',
457
459
  '})().catch(error => { process.stderr.write(error.stack || error.message); process.exit(1); });',
@@ -468,6 +470,7 @@ try {
468
470
  const supported = row.id !== 'ollama';
469
471
  if (!Array.isArray(row.results) || row.results.length !== 1
470
472
  || row.results[0].id !== row.id
473
+ || !Array.isArray(row.checked) || row.checked.length !== 1 || row.checked[0] !== row.id
471
474
  || (supported && (row.calls.length !== 1 || row.calls[0].stdin !== 'ignore' || row.results[0].ok !== true))
472
475
  || (!supported && (row.calls.length !== 0 || !/explicit model/.test(String(row.results[0].error || ''))))) {
473
476
  failures.push(`agents bench ${row.id} 단독-ready settled 결과 오류: ${JSON.stringify(row)}`);