leerness 1.35.8 → 1.35.9
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 +14 -0
- package/README.md +4 -4
- package/bin/leerness.js +25 -3
- package/package.json +1 -1
- package/scripts/e2e.js +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.35.9 — 2026-07-03 — 자체 적대적 FP 헌트: declared-pass 게이트를 완전-통과(N/N) 주장으로 한정 (1.35.7 후속 하드닝)
|
|
4
|
+
|
|
5
|
+
**1.35.7 에서 declared-pass 부풀림 게이팅을 넣은 뒤, 그 신규 게이트에 전용 FP/FN 헌트를 직접 돌림**(메모리 교훈: 「휴리스틱 적대적 하드닝 — 배포 전 bypass+FP 헌터」·「게시본 재실증」). 12-프로브 매트릭스(단일/`--json`/`--all`/`gate --claims` × 비율/카운트/spec·tap·jest 리포터/growth/lenient)를 게시본 1.35.8 에 실행 → **bypass(FN) 0, 정상 FP 0**, 단 1건의 진짜 false-positive 발견.
|
|
6
|
+
|
|
7
|
+
### 확정 FP (게시본 1.35.8 재현)
|
|
8
|
+
- **비-테스트 부분 비율 오탐**: evidence `"closed 2/5 passing PRs"`(테스트와 무관한 부분 비율) + 실행 1/1 → `declaredPass` 파서가 "2/5 passing" 을 테스트 통과 주장으로 추출 → 게이트가 1 < 2 로 **truthful 완료를 거짓 거부(exit 1)**. 검증 도구가 정직한 완료를 CI 에서 막는 최악 케이스(원래 FN 보다 신뢰 훼손 큼). 1.35.7 이 이 표시상 모호함을 게이팅으로 승격시키며 노출됨.
|
|
9
|
+
|
|
10
|
+
### 수정 (원칙적 — 위협모델 정렬)
|
|
11
|
+
- **완전-통과(N/N) 주장만 게이팅**: `_declaredFullPass = declaredPass.num === declaredPass.denom` 조건 추가. 위협모델은 거짓 "all green" 이므로 완전-통과 주장만 부풀림 판정. 부분 비율(N<M)은 완료-주장이 아니거나 비-테스트 → 게이트 제외(표시는 advisory 유지, "부분 비율 — 게이트 제외" 정직 라벨). **FN 무회귀**: 정직한 "8/12 passed" done 의 실제 부분-실행은 이미 `allPassed`(tests-failed)가 별도로 잡으므로 이 게이트가 불필요. 완전-통과 부풀림("3/3 주장 vs 2/2 실행")은 그대로 FAIL.
|
|
12
|
+
- 표시 라벨 정직화: 실행<주장이지만 부분 비율이라 게이트 제외인 경우 "실행 ≥ 주장" 오표기 대신 "부분 비율 — advisory, 게이트 제외" 로 표기.
|
|
13
|
+
|
|
14
|
+
### 검증
|
|
15
|
+
- selftest **269** (신규: N/N 게이트 종속 + 부분 비율 advisory 라벨). full e2e **382** (기존 declared-pass 케이스에 (7) 비-테스트 부분 비율 FP 가드 추가 — exit 0). 12-프로브 매트릭스 전부 OK(FN 0/FP 0)로 게시본 재실증 예정.
|
|
16
|
+
|
|
3
17
|
## 1.35.8 — 2026-07-03 — GPT5.5평가 잔여 제안 채택: 문서 drift 가드 + 전용 스캐너 병행 레시피 + 테스트 티어 안내 (UR-0017)
|
|
4
18
|
|
|
5
19
|
**GPT 5.5 Pro 평가 후속 라운드** — 1.35.7 에서 확정버그 3건을 수정했고, 이번엔 잔여 개선제안 중 저위험·고정직성 항목을 채택. 나머지(e2e 3-tier 분리 / minimal lazy-create / AST 검증기)는 UR-0014~0016 으로 백로그 등록.
|
package/README.md
CHANGED
|
@@ -125,7 +125,7 @@ MIT
|
|
|
125
125
|
<!-- leerness:project-readme:start -->
|
|
126
126
|
## Leerness Project Harness
|
|
127
127
|
|
|
128
|
-
이 프로젝트는 Leerness v1.35.
|
|
128
|
+
이 프로젝트는 Leerness v1.35.9 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
129
129
|
|
|
130
130
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
131
131
|
|
|
@@ -179,7 +179,7 @@ leerness memory restore decision <date|title>
|
|
|
179
179
|
|
|
180
180
|
### MCP server (외부 AI 통합)
|
|
181
181
|
|
|
182
|
-
Leerness v1.35.
|
|
182
|
+
Leerness v1.35.9는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
|
|
183
183
|
|
|
184
184
|
```jsonc
|
|
185
185
|
// 카테고리별
|
|
@@ -200,7 +200,7 @@ Leerness v1.35.8는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code
|
|
|
200
200
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
201
201
|
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) 다음 라운드 예약.
|
|
202
202
|
|
|
203
|
-
현재 누적: **70 라운드 (1.9.40 → 1.35.
|
|
203
|
+
현재 누적: **70 라운드 (1.9.40 → 1.35.9)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
204
204
|
|
|
205
205
|
### 성능 가이드 (1.9.140 측정)
|
|
206
206
|
|
|
@@ -238,6 +238,6 @@ leerness release pack --close --auto-main-push
|
|
|
238
238
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
239
239
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
240
240
|
|
|
241
|
-
Last synced by Leerness v1.35.
|
|
241
|
+
Last synced by Leerness v1.35.9: 2026-07-03
|
|
242
242
|
<!-- leerness:project-readme:end -->
|
|
243
243
|
|
package/bin/leerness.js
CHANGED
|
@@ -32,7 +32,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
32
32
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
33
33
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _TOOL_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 분리 · 1.11.4 (UR-0007): _TOOL_CATALOG
|
|
34
34
|
|
|
35
|
-
const VERSION = '1.35.
|
|
35
|
+
const VERSION = '1.35.9';
|
|
36
36
|
|
|
37
37
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
38
38
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -2916,6 +2916,15 @@ function _selfTestCases() {
|
|
|
2916
2916
|
const advisoryOk = /pair with a dedicated scanner \(gitleaks\/trufflehog\)/.test(read(__filename));
|
|
2917
2917
|
return managedOk && recipeOk && advisoryOk;
|
|
2918
2918
|
} },
|
|
2919
|
+
{ name: '자체 적대적 FP 헌트 (1.35.9): declared-pass 게이트는 완전-통과(N/N) 주장만 — 부분 비율(비-테스트) 오탐 차단 + 완전-통과 부풀림 무회귀', run: () => {
|
|
2920
|
+
const src = read(__filename);
|
|
2921
|
+
// 게이트가 _declaredFullPass(num===denom) 조건에 종속 — 부분 비율(N<M)은 게이팅 제외.
|
|
2922
|
+
const fullPassGuard = src.includes('const _declaredFullPass = !!(declaredPass && declaredPass.num === declaredPass.denom);')
|
|
2923
|
+
&& /_declaredPassMismatch = !lenient && _declaredFullPass &&/.test(src);
|
|
2924
|
+
// 표시 라벨은 부분 비율을 advisory(게이트 제외)로 정직 표기 — 실행<주장인데 "실행 ≥ 주장" 오표기 안 함.
|
|
2925
|
+
const honestLabel = src.includes('부분 비율 — advisory, 게이트 제외') && src.includes('partial ratio — advisory, not gated');
|
|
2926
|
+
return fullPassGuard && honestLabel;
|
|
2927
|
+
} },
|
|
2919
2928
|
{ name: 'honesty-check: AI 인식론적 정직성 3차원 + MCP/CLI/strict 통합 (사용자명시 1.9.305)', run: () => { const h = _epistemicHonestyCheck; const d1 = h('이 기능은 항상 정상 동작합니다').findings.some(f => f.dim === 'pretend-knowledge'); const d2 = h('아마 될 것 같습니다. 구현 완료했습니다').findings.some(f => f.dim === 'premature-judgment'); const d3 = h('이 API 의 rate limit 은 초당 5회입니다').findings.some(f => f.dim === 'no-info-gathering'); const clean = h('src/api.js 수정, 12/12 통과 (Exit: 0)').ok === true; const src = read(__filename); const wired = require('../lib/mcp-tools').some(t => t.name === 'leerness_honesty_check') && /if \(cmd === 'honesty-check'\)/.test(src) && /honestyFindings = _epistemicHonestyCheck/.test(src); return d1 && d2 && d3 && clean && wired; } },
|
|
2920
2929
|
{ name: 'exit code 일관성: fail()→exitCode 1 행위 + unknown 명령 안내 (UR-0045 / CV-5 행위화 1.9.366)', run: () => { if (typeof fail !== 'function') return false; const saved = process.exitCode; const _w = process.stdout.write; let setOk = false; try { process.stdout.write = () => true; process.exitCode = 0; fail('selftest probe'); setOk = process.exitCode === 1; } finally { process.stdout.write = _w; process.exitCode = saved; } const src = read(__filename); const dispatchOk = /알 수 없는 명령: \$\{cmd\}/.test(src); return setOk && dispatchOk; } },
|
|
2921
2930
|
{ name: 'brief: 프로젝트 청사진 set/show/export + README 개요 섹션 (UR-0055 사용자명시 1.9.307)', run: () => { const src = read(__filename); const fnOk = typeof briefCmd === 'function' && typeof _loadBrief === 'function' && typeof _briefBlueprint === 'function' && _BRIEF_FIELDS.length === 10; const b = { project: 'X', intro: 'i', purpose: 'p', problem: '', features: ['f1', 'f2'], stack: ['s'], architecture: '', users: [], success: [], nonGoals: [], currentState: '' }; const bp = _briefBlueprint(b, VERSION); const bpOk = /Blueprint/.test(bp) && /소개 \(Intro\)/.test(bp) && /f1/.test(bp) && /신규 프로젝트 시작 가이드/.test(bp); const rb = _briefReadmeBlock(b); const rbOk = rb.includes(BRIEF_START) && rb.includes(BRIEF_END) && /프로젝트 개요/.test(rb) && /\*\*목적\*\*/.test(rb); return fnOk && bpOk && rbOk && /if \(cmd === 'brief'\)/.test(src); } },
|
|
@@ -10689,7 +10698,13 @@ function verifyClaimCmd(root, taskId, opts = {}) {
|
|
|
10689
10698
|
// 이전: 3경로(human overallFail / --json exit / collect reasons) 모두 불일치를 감지·표시만 하고 exit 0 — 부풀린 주장이 플래그십을 통과 + 최종요약은 "일치 ✓" 자기모순.
|
|
10690
10699
|
// 주장 개수("N개")는 이미 게이팅(testCountMatch)되는데 비율 주장("N/M")만 안 되던 내부 비일관성 해소.
|
|
10691
10700
|
// 방향성 게이트(실행 pass ≥ 주장 pass 면 통과) — 테스트가 늘어난 정상 흐름을 벌하지 않음(testCountMatch 의 실측≥주장 규칙과 동일 철학). 완화: --lenient.
|
|
10692
|
-
|
|
10701
|
+
// 1.35.9 (자체 적대적 FP 헌트): declared-pass 게이트는 "전부 통과(N/N)" 완료-주장에만 적용.
|
|
10702
|
+
// FP 재현: evidence "closed 2/5 passing PRs"(비-테스트 부분 비율) + 실행 1/1 → 게이트가 truthful 완료를 거짓 거부(exit 1).
|
|
10703
|
+
// 원인: declaredPass 파서는 evidence 어디든 "N/M passing" 을 테스트 주장으로 추출 → 1.35.7 이 이 표시상 모호함을 게이팅으로 승격.
|
|
10704
|
+
// 수정: 위협모델은 거짓 "all green"(N/N) 이므로 완전-통과 주장만 게이팅. 부분 비율(N<M)은 완료-주장이 아니거나 비-테스트 → 제외(표시는 advisory 유지).
|
|
10705
|
+
// FN 무회귀: 정직한 "8/12 passed" done 의 실제 부분-실행은 allPassed(tests-failed)가 별도로 잡음(이 게이트 불필요).
|
|
10706
|
+
const _declaredFullPass = !!(declaredPass && declaredPass.num === declaredPass.denom);
|
|
10707
|
+
const _declaredPassMismatch = !lenient && _declaredFullPass && !!(runResult && !runResult.skipped && runResult.parsed && runResult.parsed.num < declaredPass.num);
|
|
10693
10708
|
|
|
10694
10709
|
// 1.33.2 (verify-claim --all): 집계 모드는 렌더/exit 없이 verdict 만 반환. 게이팅 부울은 아래 --json/human 경로와 동일 계산을 공유(분기 없음).
|
|
10695
10710
|
if (opts.collect) {
|
|
@@ -10806,7 +10821,14 @@ function verifyClaimCmd(root, taskId, opts = {}) {
|
|
|
10806
10821
|
if (runResult && !runResult.skipped) {
|
|
10807
10822
|
// 1.27.1 (13번째 외부리뷰 #3): exit 0 인데 테스트 비율을 못 파싱한 경우(예: 비-테스트 --test-cmd)를 '✓ all passed' 로 거짓표기하지 않음 — '실행됨, 테스트 수 미확인' 으로 정직 표기(판정/exit 불변 → 이색 테스트러너 FP 없음).
|
|
10808
10823
|
log(` - ${runResult.cmd || 'npm test'} ${t('실행', 'run')}: ${runTestsOk ? (runResult.parsed ? '✓ all passed' : t('✓ 실행됨 (exit 0) — 테스트 수 미확인', '✓ ran (exit 0) — test count unconfirmed')) : '✗ FAIL'}`);
|
|
10809
|
-
if (declaredPass)
|
|
10824
|
+
if (declaredPass) { // 1.35.7 (UR-0013): 자기모순 표기 제거 — 부풀림은 FAIL 라벨. 1.35.9: 부분 비율은 advisory(게이트 제외) 정직 표기.
|
|
10825
|
+
let _vsLabel;
|
|
10826
|
+
if (declaredPassMatchesActual) _vsLabel = '✓ pass';
|
|
10827
|
+
else if (_declaredPassMismatch) _vsLabel = t('✗ FAIL (주장이 실행 결과보다 부풀려짐)', '✗ FAIL (claim inflated vs run)');
|
|
10828
|
+
else if (_declaredFullPass) _vsLabel = t('⚠ 다름 (실행 ≥ 주장 — pass)', '⚠ differs (run ≥ claimed — pass)');
|
|
10829
|
+
else _vsLabel = t('⚠ 다름 (부분 비율 — advisory, 게이트 제외)', '⚠ differs (partial ratio — advisory, not gated)');
|
|
10830
|
+
log(` - ${t('주장과 실행 결과 일치', 'claimed matches run')}: ${_vsLabel}`);
|
|
10831
|
+
}
|
|
10810
10832
|
}
|
|
10811
10833
|
// 1.11.2 (UR-0175): optimism+정직성 — done 주장은 기본 게이팅(claimsChecked). 완화: --lenient.
|
|
10812
10834
|
if (claimsChecked) {
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -6848,9 +6848,13 @@ total++;
|
|
|
6848
6848
|
const r6 = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0002', '--run-tests', '--test-cmd', 'echo Tests: 3 passed, 3 total', '--path', d], { encoding: 'utf8', timeout: 20000 });
|
|
6849
6849
|
const truthfulOk = r5.status === 0;
|
|
6850
6850
|
const growthOk = r6.status === 0;
|
|
6851
|
+
// (7) 1.35.9 FP 가드: 비-테스트 부분 비율("2/5 passing PRs") + 실행 1/1 → 게이트 제외(exit 0). 완전-통과 부풀림만 게이팅.
|
|
6852
|
+
cp.spawnSync(process.execPath, [CLI, 'task', 'update', 'T-0002', '--status', 'done', '--evidence', 'src/x.js implemented, x.test.js added; closed 2/5 passing PRs', '--path', d], { encoding: 'utf8', timeout: 15000 });
|
|
6853
|
+
const r7 = cp.spawnSync(process.execPath, [CLI, 'verify-claim', 'T-0002', '--run-tests', '--test-cmd', 'echo 1 passing', '--path', d], { encoding: 'utf8', timeout: 20000 });
|
|
6854
|
+
const partialRatioFpOk = r7.status === 0; // 부분 비율은 게이팅 안 함(오탐 차단)
|
|
6851
6855
|
fs.rmSync(d, { recursive: true, force: true });
|
|
6852
|
-
ok = inflatedFails && jsonOk && allOk && specParsed && truthfulOk && growthOk;
|
|
6853
|
-
if (!ok) console.log(` [dpm 디버그] inflated=${inflatedFails} json=${jsonOk} all=${allOk} spec=${specParsed} truthful=${truthfulOk} growth=${growthOk}`);
|
|
6856
|
+
ok = inflatedFails && jsonOk && allOk && specParsed && truthfulOk && growthOk && partialRatioFpOk;
|
|
6857
|
+
if (!ok) console.log(` [dpm 디버그] inflated=${inflatedFails} json=${jsonOk} all=${allOk} spec=${specParsed} truthful=${truthfulOk} growth=${growthOk} partialFP=${partialRatioFpOk}`);
|
|
6854
6858
|
} catch {}
|
|
6855
6859
|
console.log(ok ? '✓ B(1.35.7) UR-0013: declared-pass 부풀림 게이팅(human/json/--all) + spec 리포터 파싱 + 실행≥주장 무벌점' : '✗ declared-pass mismatch 게이팅 실패');
|
|
6856
6860
|
if (!ok) failed++;
|