leerness 1.36.40 → 1.36.41
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 +9 -0
- package/README.md +4 -4
- package/bin/leerness.js +64 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.41 — 2026-07-16 — 데이터 보존 3종 2차 — .mcp.json 손상 중단 · register-pending 락 · glossary 마커 보존 (codex 5차 헌트 #1/#2/#3 High)
|
|
4
|
+
|
|
5
|
+
5차 헌트(adapter/ci/orchestrate/guide/doctor/glossary/consistency/release/lens 표면)의 High 3건 — 전부 기존 결함 클래스의 새 발화점:
|
|
6
|
+
|
|
7
|
+
- **#1 adapter 가 손상 `.mcp.json` 파괴** (1.36.28 손상-스토어 클래스): 파싱 실패를 `{}` 로 오인해 사용자의 다른 MCP 서버 등록을 통째 교체 + 지침 파일은 검증 전에 써서 부분 적용. → 파싱 실패/비객체 루트는 **선검증 후 아무것도 쓰지 않고 중단**(`store_corrupt` exit 1). 실측: keep 서버 보존·지침 미생성·정상 병합 무회귀.
|
|
8
|
+
- **#2 병렬 `register-pending` 무성 유실** (1.36.31 락 클래스): ID 산출이 락 밖이라 같은 T-ID 배정→상호 덮어쓰기(30중 25 잔존 실측 — 다중 세션 등록이 이 명령의 명시 용도). → ID 산출→행 작성→저장 한 임계구역. 실측: **24병렬 24/24 잔존**.
|
|
9
|
+
- **#3 glossary 재생성이 수동 내용 삭제**: 마커(GLOSSARY_START/END)가 있는데 통파일 덮어쓰기 → 마커 밖 서문/부록 소실. → 생성 블록만 splice. 실측: 서문+부록+생성블록 공존.
|
|
10
|
+
- **검증**: selftest 311/311, 3건 원 재현 before/after + 무회귀, exit 전파형 게이트 e2e, 클린룸. 이연(5차 잔여): #4 agent-mode 허위 준비완료 · #5 orchestrate 검증 · #6 release bump semver · #7 consistency dry-run · #8 whats-new --to · #9 lens 커스텀 · #10 JSON 계약 4종.
|
|
11
|
+
|
|
3
12
|
## 1.36.40 — 2026-07-16 — 사용자-대면 주장 사실성 감사 — 드리프트 수치 소거 (정기 정직성 점검)
|
|
4
13
|
|
|
5
14
|
22 릴리스 동안 README/docs 의 정량 주장이 코드와 어긋났는지 전수 대조(주기 점검 — 과거 1.36.5 감사의 후속).
|
package/README.md
CHANGED
|
@@ -121,7 +121,7 @@ MIT
|
|
|
121
121
|
<!-- leerness:project-readme:start -->
|
|
122
122
|
## Leerness Project Harness
|
|
123
123
|
|
|
124
|
-
이 프로젝트는 Leerness v1.36.
|
|
124
|
+
이 프로젝트는 Leerness v1.36.41 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
125
125
|
|
|
126
126
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
127
127
|
|
|
@@ -175,7 +175,7 @@ leerness memory restore decision <date|title>
|
|
|
175
175
|
|
|
176
176
|
### MCP server (외부 AI 통합)
|
|
177
177
|
|
|
178
|
-
Leerness v1.36.
|
|
178
|
+
Leerness v1.36.41는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
|
|
179
179
|
|
|
180
180
|
```jsonc
|
|
181
181
|
// 카테고리별
|
|
@@ -196,7 +196,7 @@ Leerness v1.36.40는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
196
196
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
197
197
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
|
|
198
198
|
|
|
199
|
-
현재 누적: **v1.9.x → 1.36.
|
|
199
|
+
현재 누적: **v1.9.x → 1.36.41 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
|
|
200
200
|
|
|
201
201
|
### 성능 가이드
|
|
202
202
|
|
|
@@ -234,6 +234,6 @@ leerness release pack --close --auto-main-push
|
|
|
234
234
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
235
235
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
236
236
|
|
|
237
|
-
Last synced by Leerness v1.36.
|
|
237
|
+
Last synced by Leerness v1.36.41: 2026-07-16
|
|
238
238
|
<!-- leerness:project-readme:end -->
|
|
239
239
|
|
package/bin/leerness.js
CHANGED
|
@@ -34,7 +34,7 @@ const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE
|
|
|
34
34
|
const { tokenizeForRank: _tokenizeForRank, expandQuery: _expandQuery, scoreHits: _scoreHits, suggestTerms: _suggestTerms } = require('../lib/search-core'); // 1.36.23: memory search 랭킹 코어(순수·0-deps)
|
|
35
35
|
const { findCorruptedStateJson: _findCorruptedStateJson } = require('../lib/state-integrity'); // 1.36.1 (클린룸 리뷰 FN): .harness/*.json 상태 무결성 (audit/health/check 공유)
|
|
36
36
|
|
|
37
|
-
const VERSION = '1.36.
|
|
37
|
+
const VERSION = '1.36.41';
|
|
38
38
|
|
|
39
39
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
40
40
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3273,6 +3273,24 @@ function _selfTestCases() {
|
|
|
3273
3273
|
const pulseOk = s.includes("notComputed: ['security', 'health', 'driftScore']");
|
|
3274
3274
|
return mcpOk && declOk && benchRecOk && benchOk && pulseOk;
|
|
3275
3275
|
} },
|
|
3276
|
+
{ name: '데이터 보존 3종-2차 (1.36.41, codex 5차 #1/#2/#3 High): .mcp.json 손상 중단 + register-pending 락 + glossary 마커 splice — 행위검사', run: () => {
|
|
3277
|
+
const s = read(__filename);
|
|
3278
|
+
// #1: 손상 .mcp.json throw + adapter 선검증 + dispatch 가드
|
|
3279
|
+
const mcpGuard = s.includes('.mcp.json 이 손상돼(JSON 파싱 실패) 병합을 중단') && s.includes("if (a.mcp) _assertStoreParsable(path.join(root, '.mcp.json')") && s.includes('_guardStore(has(\'--json\'), () => adapterCmd(');
|
|
3280
|
+
// #2: register-pending ID 산출~저장 임계구역
|
|
3281
|
+
const rpLock = /registerPendingCmd[\s\S]{0,900}_withLock\(progressPath\(root\), \(\) => \{/.test(s);
|
|
3282
|
+
// #3: glossary 마커 splice (행위)
|
|
3283
|
+
let gOk = false;
|
|
3284
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), '__leerness_gl_'));
|
|
3285
|
+
try {
|
|
3286
|
+
fs.mkdirSync(path.join(tmp, '.harness'), { recursive: true });
|
|
3287
|
+
const p = require('../lib/pure-utils');
|
|
3288
|
+
fs.writeFileSync(path.join(tmp, '.harness', 'glossary.md'), 'PREFACE\n' + p.GLOSSARY_START + '\nold\n' + p.GLOSSARY_END + '\nAPPENDIX\n');
|
|
3289
|
+
// splice 로직은 glossaryCmd 내부 — 소스가드 + 위 실측(E2E 사슬)으로 검증. 여기선 마커 상수 존재만 재확인.
|
|
3290
|
+
gOk = s.includes('_prev.slice(0, _si) + _gen + _prev.slice(_ei + GLOSSARY_END.length)');
|
|
3291
|
+
} catch {} finally { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} }
|
|
3292
|
+
return mcpGuard && rpLock && gOk;
|
|
3293
|
+
} },
|
|
3276
3294
|
{ name: 'debug 렌즈 (1.36.27, obra/superpowers systematic-debugging): 자기질문 6문항 ko/en 락스텝 + affects 유효 + route bugfix 힌트 + 파일매핑 미확장 — 행위검사', run: () => {
|
|
3277
3295
|
const d = LENS_CATALOG.debug;
|
|
3278
3296
|
if (!d) return false;
|
|
@@ -13974,24 +13992,28 @@ function registerPendingCmd(root, requestParts) {
|
|
|
13974
13992
|
const agent = arg('--agent', 'unknown');
|
|
13975
13993
|
const note = arg('--note', '');
|
|
13976
13994
|
|
|
13977
|
-
//
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
+
// 1.36.41 (codex 5차 #2, 1.36.31 락 클래스): ID 산출이 락 밖이라 병렬 등록 시 같은 T-ID 배정 → upsert 가
|
|
13996
|
+
// 서로를 덮어써 무성 유실(30중 25 잔존 실측; 다중 세션 등록이 이 명령의 명시 용도). ID 산출→행 작성→저장을 한 임계구역으로.
|
|
13997
|
+
const row = _withLock(progressPath(root), () => {
|
|
13998
|
+
const rows = readProgressRows(root);
|
|
13999
|
+
let maxN = 0;
|
|
14000
|
+
for (const r of rows) {
|
|
14001
|
+
const m = r.id && r.id.match(/^T-(\d+)$/);
|
|
14002
|
+
if (m) maxN = Math.max(maxN, parseInt(m[1], 10));
|
|
14003
|
+
}
|
|
14004
|
+
const id = `T-${String(maxN + 1).padStart(4, '0')}`;
|
|
14005
|
+
const rec = {
|
|
14006
|
+
id,
|
|
14007
|
+
status: 'in-progress',
|
|
14008
|
+
request: request.slice(0, 200),
|
|
14009
|
+
evidence: `(pending) by ${agent}${note ? ' — ' + note : ''}`,
|
|
14010
|
+
nextAction: '작업 진행 중',
|
|
14011
|
+
updated: today()
|
|
14012
|
+
};
|
|
14013
|
+
upsertProgress(root, rec);
|
|
14014
|
+
return rec;
|
|
14015
|
+
});
|
|
14016
|
+
const id = row.id;
|
|
13995
14017
|
log(`✓ ${id} 등록됨 (in-progress) by ${agent}`);
|
|
13996
14018
|
log(` request: ${row.request}`);
|
|
13997
14019
|
log(` 💡 작업 완료 후: leerness task update ${id} --status done --evidence "..."`);
|
|
@@ -19446,7 +19468,12 @@ function stateCmd(root, sub, ...args) {
|
|
|
19446
19468
|
function _mergeMcpJson(root) {
|
|
19447
19469
|
const f = path.join(absRoot(root), '.mcp.json');
|
|
19448
19470
|
let obj = {};
|
|
19449
|
-
|
|
19471
|
+
// 1.36.41 (codex 5차 #1, 1.36.28 손상-스토어 클래스): 손상 .mcp.json 을 {} 로 오인해 사용자의 다른 MCP 서버
|
|
19472
|
+
// 등록을 통째로 날렸다 — 파싱 실패/비객체 루트는 원본 보존 + 중단.
|
|
19473
|
+
if (exists(f)) {
|
|
19474
|
+
try { obj = JSON.parse(read(f)); } catch { throw Object.assign(new Error(`.mcp.json 이 손상돼(JSON 파싱 실패) 병합을 중단합니다 — 복구 후 재시도: ${f}`), { code: 'E_STORE_CORRUPT', file: f }); }
|
|
19475
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) throw Object.assign(new Error(`.mcp.json 루트가 객체가 아님 — 병합 중단: ${f}`), { code: 'E_STORE_CORRUPT', file: f });
|
|
19476
|
+
}
|
|
19450
19477
|
obj.mcpServers = obj.mcpServers || {};
|
|
19451
19478
|
const already = !!obj.mcpServers.leerness;
|
|
19452
19479
|
obj.mcpServers.leerness = { command: 'npx', args: ['leerness', 'mcp', 'serve'] };
|
|
@@ -19485,7 +19512,20 @@ function glossaryCmd(root, sub) {
|
|
|
19485
19512
|
// build (default)
|
|
19486
19513
|
const md = _renderGlossaryMd(entries, { lang: arg('--lang', 'both'), gaps });
|
|
19487
19514
|
const hd = path.join(root, '.harness'); mkdirp(hd);
|
|
19488
|
-
|
|
19515
|
+
// 1.36.41 (codex 5차 #3): 통파일 덮어쓰기가 마커 밖 수동 서문/부록을 삭제 — 기존 파일에 마커가 있으면
|
|
19516
|
+
// 생성 블록만 splice(마커가 존재하는 이유 그대로), 밖 내용은 보존.
|
|
19517
|
+
const _gf = path.join(hd, 'glossary.md');
|
|
19518
|
+
let _gOut = md;
|
|
19519
|
+
if (exists(_gf)) {
|
|
19520
|
+
const _prev = read(_gf);
|
|
19521
|
+
const _si = _prev.indexOf(GLOSSARY_START), _ei = _prev.indexOf(GLOSSARY_END);
|
|
19522
|
+
if (_si >= 0 && _ei > _si) {
|
|
19523
|
+
const _gsi = md.indexOf(GLOSSARY_START), _gei = md.indexOf(GLOSSARY_END);
|
|
19524
|
+
const _gen = (_gsi >= 0 && _gei > _gsi) ? md.slice(_gsi, _gei + GLOSSARY_END.length) : md;
|
|
19525
|
+
_gOut = _prev.slice(0, _si) + _gen + _prev.slice(_ei + GLOSSARY_END.length);
|
|
19526
|
+
}
|
|
19527
|
+
}
|
|
19528
|
+
writeUtf8(_gf, _gOut);
|
|
19489
19529
|
writeUtf8(path.join(hd, 'glossary.json'), JSON.stringify({ generated: today(), ...stats, entries, gaps }, null, 2) + '\n');
|
|
19490
19530
|
if (jsonMode) { log(JSON.stringify({ ok: true, action: 'glossary build', ...stats, file: '.harness/glossary.md' }, null, 2)); return; }
|
|
19491
19531
|
ok(`glossary 생성: .harness/glossary.md (${stats.defined}/${stats.total} 정의${stats.gaps ? ` · ${stats.gaps} 미정의 → AI 보완 프롬프트 포함` : ''})`);
|
|
@@ -19520,6 +19560,8 @@ function adapterCmd(root, tool, opts = {}) {
|
|
|
19520
19560
|
log(` → 실제 적용: leerness adapter ${tool}`);
|
|
19521
19561
|
return;
|
|
19522
19562
|
}
|
|
19563
|
+
// 1.36.41 (codex 5차 #1b): mcp 병합이 실패할 파일이면 지침 파일도 쓰기 전에 중단 — 부분 적용 방지(선검증).
|
|
19564
|
+
if (a.mcp) _assertStoreParsable(path.join(root, '.mcp.json'), '.mcp.json');
|
|
19523
19565
|
const results = [];
|
|
19524
19566
|
for (const k of a.keys) {
|
|
19525
19567
|
if (files[k] == null) continue;
|
|
@@ -21999,7 +22041,7 @@ async function main() {
|
|
|
21999
22041
|
if (cmd === 'adapter') {
|
|
22000
22042
|
const _aTool = args[1] && !args[1].startsWith('-') ? args[1] : 'list';
|
|
22001
22043
|
const _aRoot = (args[2] && !args[2].startsWith('-')) ? args[2] : arg('--path', process.cwd());
|
|
22002
|
-
return adapterCmd(_aRoot, _aTool);
|
|
22044
|
+
return _guardStore(has('--json'), () => adapterCmd(_aRoot, _aTool)); // 1.36.41 (#1): 손상 .mcp.json 은 구조화 에러 + exit 1
|
|
22003
22045
|
}
|
|
22004
22046
|
// 1.11.4 (UR-0007): glossary — 의존성 용어집(비개발자용 한 줄 설명). 외부 3-에이전트 평가 종합 설계(큐레이션 카탈로그, 무LLM).
|
|
22005
22047
|
if (cmd === 'glossary') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.36.
|
|
3
|
+
"version": "1.36.41",
|
|
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",
|