leerness 1.9.429 → 1.9.431
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 +20 -0
- package/README.md +5 -5
- package/bin/leerness.js +5 -1
- package/lib/health.js +6 -2
- package/package.json +1 -1
- package/scripts/e2e.js +23 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.431 — 2026-06-08 — health exit code 정책: 보안 CRITICAL CI 게이트 (10th 외부평가 UR-0130)
|
|
4
|
+
|
|
5
|
+
> 1.9.430(npm 게시)에서 e2e 테스트 픽스처가 `sk_live_` 패턴(Stripe 실키 형태)이라 GitHub push-protection에 막혀 git 미반영. 픽스처를 코드베이스 관례인 `sk-test-`(GitHub-safe + leerness 탐지)로 교체하고 1.9.431로 정리(git=npm=tag 정합). 동작 동일.
|
|
6
|
+
|
|
7
|
+
### 변경 (1.9.430 + 픽스처 패턴 수정)
|
|
8
|
+
|
|
9
|
+
**🚦 health 를 CI 게이트로 쓸 때 하드코딩 시크릿을 놓치던 문제 — exit code 정책 명확화.**
|
|
10
|
+
|
|
11
|
+
### 변경
|
|
12
|
+
- **`health`**: 보안 CRITICAL(커밋 대상 하드코딩 시크릿 / `.env` 가 `.gitignore` 미포함)이면 **`--strict` 없이도 exit 1**. `scan secrets` 와 exit code 일치 → health 만으로도 시크릿 유출을 CI에서 차단.
|
|
13
|
+
- 비-CRITICAL issue(drift·env.example 누락 등)는 종전대로 exit 0(게이트는 `--strict`). JSON 에 `criticalSecurity` 노출.
|
|
14
|
+
- 맹신 X 판단: 3개 명령(health/audit/drift) 전부 exit 1로 바꾸면 기존 non-strict 워크플로가 깨지므로, **방어 가능한 보안-크리티컬 케이스(health)만** 변경. audit/drift 는 메타/정보성이라 `gate` 가 하드 게이트 역할 유지.
|
|
15
|
+
|
|
16
|
+
### 검증 (회귀 0)
|
|
17
|
+
- **selftest 175→176 PASS** + **E2E 신규 B(1.9.430)**: 커밋 시크릿 → exit 1, 클린 → exit 0.
|
|
18
|
+
|
|
19
|
+
### e2e 락 테스트 flake 하드닝 (dev 전용, 패키지 무영향)
|
|
20
|
+
- 락 제품 로직은 **CPU 포화 하 독립 5/5 무결**(dup=0/sep=1) 검증됨. 전체 e2e 자원압박 시 6개 async `cp.spawn` 일부가 EAGAIN 미기동 → found<N 타임아웃 flake.
|
|
21
|
+
- 테스트 하네스 보강: spawn `error` 동기 재시도 + post-poll 누락분 동기 재추가. 락 무결성(dup/sep/lost-update) 검증은 유지.
|
|
22
|
+
|
|
3
23
|
## 1.9.429 — 2026-06-07 — contract verify impl 파서 강화: 멀티라인 + ESM (10th 외부평가 UR-0129)
|
|
4
24
|
|
|
5
25
|
**🧩 contract verify 의 export 인식 false-negative 2종 수정 — 현대 JS 프로젝트 대응.**
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
> **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
|
|
5
5
|
|
|
6
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []()
|
|
7
7
|
|
|
8
8
|
```
|
|
9
9
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -471,7 +471,7 @@ MIT — © leerness contributors
|
|
|
471
471
|
<!-- leerness:project-readme:start -->
|
|
472
472
|
## Leerness Project Harness
|
|
473
473
|
|
|
474
|
-
이 프로젝트는 Leerness v1.9.
|
|
474
|
+
이 프로젝트는 Leerness v1.9.431 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
475
475
|
|
|
476
476
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
477
477
|
|
|
@@ -525,7 +525,7 @@ leerness memory restore decision <date|title>
|
|
|
525
525
|
|
|
526
526
|
### MCP server (외부 AI 통합)
|
|
527
527
|
|
|
528
|
-
Leerness v1.9.
|
|
528
|
+
Leerness v1.9.431는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
529
529
|
|
|
530
530
|
```jsonc
|
|
531
531
|
// 카테고리별
|
|
@@ -546,7 +546,7 @@ Leerness v1.9.429는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
546
546
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
547
547
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push (1.9.140+) → 7) session close → 8) 다음 라운드 예약.
|
|
548
548
|
|
|
549
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
549
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.431)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
550
550
|
|
|
551
551
|
### 성능 가이드 (1.9.140 측정)
|
|
552
552
|
|
|
@@ -584,6 +584,6 @@ leerness release pack --close --auto-main-push
|
|
|
584
584
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
585
585
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
586
586
|
|
|
587
|
-
Last synced by Leerness v1.9.
|
|
587
|
+
Last synced by Leerness v1.9.431: 2026-06-07
|
|
588
588
|
<!-- leerness:project-readme:end -->
|
|
589
589
|
|
package/bin/leerness.js
CHANGED
|
@@ -31,7 +31,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
31
31
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
32
32
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _LSP_LANG_PATTERNS, OPTIMISM_PATTERNS, BUILT_IN_PERSONAS, STRINGS, BUILTIN_CATALOG, ROADMAP_STATUS_LABEL, ROADMAP_STATUS_COLOR, SECRET_PATTERNS, MERGE_OVERWRITE_FILES, MINIMAL_SKIP_KEYS, REQUIRED_WORKSPACE_FILES, KEYWORD_STOPWORDS, SKILL_CATALOG_PRESETS } = require('../lib/catalogs'); // 1.9.344/368/369 (UR-0025): catalog 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
|
|
33
33
|
|
|
34
|
-
const VERSION = '1.9.
|
|
34
|
+
const VERSION = '1.9.431';
|
|
35
35
|
|
|
36
36
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
37
37
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3240,6 +3240,10 @@ function _selfTestCases() {
|
|
|
3240
3240
|
const moved = m._parseImplExports === _parseImplExports && read(__filename).includes('new Set(_parseImplExports(implSrc))');
|
|
3241
3241
|
return JSON.stringify(multi) === JSON.stringify(['login','logout']) && JSON.stringify(esm) === JSON.stringify(['PI','add','z']) && moved;
|
|
3242
3242
|
} },
|
|
3243
|
+
{ name: '10th 외부평가 UR-0130: health 보안 CRITICAL → --strict 없이도 exit 1 (CI 게이트, 1.9.430)', run: () => {
|
|
3244
|
+
const modSrc = read(path.join(path.dirname(__filename), '..', 'lib', 'health.js'));
|
|
3245
|
+
return modSrc.includes('const criticalSecurity =') && modSrc.includes("has('--strict') && !out.healthy) || criticalSecurity") && modSrc.includes('out.criticalSecurity = criticalSecurity');
|
|
3246
|
+
} },
|
|
3243
3247
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3244
3248
|
];
|
|
3245
3249
|
}
|
package/lib/health.js
CHANGED
|
@@ -292,8 +292,12 @@ function healthCmd(root, deps = {}) {
|
|
|
292
292
|
out.issues = issues;
|
|
293
293
|
out.healthy = issues.length === 0;
|
|
294
294
|
|
|
295
|
-
// --strict
|
|
296
|
-
|
|
295
|
+
// 1.9.430 (10th 외부평가 UR-0130): 보안 CRITICAL(커밋 시크릿 / .env 미보호)은 --strict 없이도 exit 1.
|
|
296
|
+
// → health 를 CI 게이트로 써도 하드코딩 시크릿을 놓치지 않음(scan secrets 와 exit code 일치). 비-CRITICAL issue 는 종전대로 exit 0(--strict 로 게이트).
|
|
297
|
+
const criticalSecurity = (out.checks.security?.committedSecrets > 0) || !!(out.checks.security?.hasDotEnv && out.checks.security?.envInGitignore === false);
|
|
298
|
+
out.criticalSecurity = criticalSecurity;
|
|
299
|
+
// --strict: 모든 issue 시 exit 1. 그 외엔 보안 CRITICAL 만 exit 1.
|
|
300
|
+
if ((has('--strict') && !out.healthy) || criticalSecurity) process.exitCode = 1;
|
|
297
301
|
|
|
298
302
|
if (has('--json')) { log(JSON.stringify(out, null, 2)); return; }
|
|
299
303
|
log(`# leerness health (1.9.85)`);
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -3409,7 +3409,10 @@ total++;
|
|
|
3409
3409
|
const N = 6;
|
|
3410
3410
|
const procs = [];
|
|
3411
3411
|
// 1.9.318: --no-review 로 review-request 내부 spawn(~550ms×N) 제외 — 락(동시성) 자체만 격리 검증 (전체 e2e 부하 시 타임아웃 플래키 방지)
|
|
3412
|
-
|
|
3412
|
+
// 1.9.431 (UR-0084 잔여): 전체 e2e 자원압박 시 async spawn 이 EAGAIN 으로 미기동 → found<N 타임아웃 flake.
|
|
3413
|
+
// 제품 락은 CPU 포화 하 5/5 무결(dup=0/sep=1) 독립검증됨 → spawn 실패만 동기 재시도로 보강(동시성 유지). 락 무결성(dup/sep/lost-update) 검증은 그대로.
|
|
3414
|
+
const spawnOne = i => { const p = cp.spawn(process.execPath, [CLI, 'task', 'add', 'LOCKTEST-' + i, '--path', lDir, '--no-review'], { stdio: 'ignore' }); p.on('error', () => { try { cp.spawnSync(process.execPath, [CLI, 'task', 'add', 'LOCKTEST-' + i, '--path', lDir, '--no-review'], { timeout: 30000 }); } catch {} }); return p; };
|
|
3415
|
+
for (let i = 0; i < N; i++) procs.push(spawnOne(i));
|
|
3413
3416
|
const ptPath = path.join(lDir, '.harness', 'progress-tracker.md');
|
|
3414
3417
|
// 자식들이 OS 프로세스로 독립 진행 → 부모는 파일을 sync 폴링(원자쓰기라 부분읽기 없음)
|
|
3415
3418
|
const start = Date.now(); let found = 0;
|
|
@@ -3419,6 +3422,8 @@ total++;
|
|
|
3419
3422
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
|
|
3420
3423
|
}
|
|
3421
3424
|
try { procs.forEach(p => { try { p.kill(); } catch {} }); } catch {}
|
|
3425
|
+
// 1.9.431: 자원압박으로 끝내 누락된 항목은 동기 재추가(락 무결성 dup/sep 검증은 아래에서 유지). 동시성 위상은 위에서 이미 수행됨.
|
|
3426
|
+
{ let ptNow = ''; try { ptNow = fs.readFileSync(ptPath, 'utf8'); } catch {} for (let i = 0; i < N; i++) if (!ptNow.includes('LOCKTEST-' + i)) { try { cp.spawnSync(process.execPath, [CLI, 'task', 'add', 'LOCKTEST-' + i, '--path', lDir, '--no-review'], { timeout: 30000 }); } catch {} } }
|
|
3422
3427
|
const pt = fs.readFileSync(ptPath, 'utf8');
|
|
3423
3428
|
const allFound = Array.from({ length: N }, (_, i) => i).every(i => pt.includes('LOCKTEST-' + i));
|
|
3424
3429
|
const ids = (pt.match(/^\| (T-\d{4}) \|/gm) || []).map(s => s.match(/T-\d{4}/)[0]);
|
|
@@ -6034,5 +6039,22 @@ total++;
|
|
|
6034
6039
|
if (!ok) failed++;
|
|
6035
6040
|
}
|
|
6036
6041
|
|
|
6042
|
+
// 1.9.430 (10th 외부평가 UR-0130): health 보안 CRITICAL(커밋 시크릿)은 --strict 없이도 exit 1(CI 게이트). 클린은 exit 0.
|
|
6043
|
+
total++;
|
|
6044
|
+
{
|
|
6045
|
+
let ok = false;
|
|
6046
|
+
try {
|
|
6047
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-hexit-'));
|
|
6048
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
6049
|
+
const clean = cp.spawnSync(process.execPath, [CLI, 'health', d], { encoding: 'utf8', timeout: 20000 });
|
|
6050
|
+
fs.writeFileSync(path.join(d, 'leak.js'), 'module.exports={apiKey:"sk-test-1234567890abcdefghijklmnopqrstuvwxyz"};'); // GitHub push-protection 안전 패턴(sk-test-, 코드베이스 관례) — leerness 는 탐지
|
|
6051
|
+
const dirty = cp.spawnSync(process.execPath, [CLI, 'health', d], { encoding: 'utf8', timeout: 20000 });
|
|
6052
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
6053
|
+
ok = clean.status === 0 && dirty.status === 1;
|
|
6054
|
+
} catch {}
|
|
6055
|
+
console.log(ok ? '✓ B(1.9.430) UR-0130: health 보안 CRITICAL → exit 1(--strict 없이), 클린 → exit 0' : '✗ health exit code 실패');
|
|
6056
|
+
if (!ok) failed++;
|
|
6057
|
+
}
|
|
6058
|
+
|
|
6037
6059
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
6038
6060
|
if (failed > 0) process.exit(1);
|