leerness 1.36.19 → 1.36.20
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 +10 -0
- package/README.md +4 -4
- package/bin/leerness.js +43 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.20 — 2026-07-14 — `--apply --json`이 실제로 적용하도록 수정 — mutation-before-output (codex fresh-QA #7, 3커맨드 재현확정)
|
|
4
|
+
|
|
5
|
+
codex fresh-surface QA(1.36.19 이연분)에서 확정한 **silent no-op** 버그 수정: `--json` 분기가 mutation "전"에 return 해, `--apply --json`이 적용을 보고하고도 실제로는 아무 변경도 안 하던 3개 커맨드.
|
|
6
|
+
|
|
7
|
+
- **`reuse autodetect --apply --json`** — 후보만 보고하고 reuse-map.md를 안 씀(bin:20307의 --json return이 20319의 --apply 앞). → apply를 출력 전에 수행하고 JSON에 `applied`/`added` 반영. 실측: `--apply --json` → `applied:true, added:2` + reuse-map 실제 기록(0→1건), `--apply`(no-json) 무회귀.
|
|
8
|
+
- **`release cleanup --apply --json`** — `apply:true, deleteCount:N`을 보고하고도 브랜치를 안 지움(15018의 --json return이 15051의 삭제 앞). → 삭제를 출력 전에 수행하고 JSON에 `deleted`/`deleteFailed` 반영 + 삭제 실패 시 `exitCode=1`. 실측: `--apply --keep 1 --json` → `deleted:3` + 브랜치 실제 삭제(4→1).
|
|
9
|
+
- **`env encoding --apply --json`** — read-only 파일 apply 실패를 `action:'failed'`로 기록만 하고 exit 0. → 실패 시 `process.exitCode=1`. 실측: read-only→exit 1, writable→exit 0.
|
|
10
|
+
- **검증**: selftest 290/290(신규 소스가드 — 3커맨드의 apply-before-output 구조 + 실패 exit 반영), 각 커맨드 재현확정(임시 프로젝트/git-repo/read-only 파일), 게이트 e2e, 게시본 클린룸.
|
|
11
|
+
- **이연(다음 라운드)**: codex #8(CRLF `plan.md` 태스크 파서 tasks:[] + 잘못된 JSON 스키마가 `--json` 경로 crash — 파서 서브시스템 별도 조사) + #6(`copyRec` lstatSync 심링크).
|
|
12
|
+
|
|
3
13
|
## 1.36.19 — 2026-07-14 — 전략 앵커(project-brief/plan Goal) 미작성 감지 — 인계 AI가 프로젝트 맥락을 못 받던 근본원인 표면화 (실사용 7 프로젝트 dogfood)
|
|
4
14
|
|
|
5
15
|
**현장 관찰**: leerness를 오래 적용한 실프로젝트에서, 인계받은 AI(codex 등)가 "leerness를 참조하나 프로젝트 맥락을 이해 못 하고" 작업하는 현상이 보고됨. 7개 실사용 프로젝트를 dogfood 조사한 결과 근본원인이 드러남 — **동적 상태(current-state 7/7, decisions)는 잘 유지되나, 정체성 앵커인 `project-brief.md` Purpose(5/7 미작성)와 `plan.md` Goal/Scope(7/7 미작성)이 템플릿 placeholder 그대로 방치됨**. 27개 결정을 쌓은 프로젝트조차 brief는 빈칸. 그 결과 인계 AI는 "최근 무슨 작업을 했는지"(동적)는 받지만 "이 프로젝트가 무엇이고 범위가 어디까지인지"(정체성)를 못 받아, 최근 스레드만 이어가며 전체 프레임을 놓침. 게다가 leerness 스스로 이 갭을 전혀 경고하지 않았음(감지 부재).
|
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.36.
|
|
128
|
+
이 프로젝트는 Leerness v1.36.20 하네스를 사용합니다. 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.36.
|
|
182
|
+
Leerness v1.36.20는 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.36.19는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
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.36.
|
|
203
|
+
현재 누적: **70 라운드 (1.9.40 → 1.36.20)** · 매 라운드 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.36.
|
|
241
|
+
Last synced by Leerness v1.36.20: 2026-07-14
|
|
242
242
|
<!-- leerness:project-readme:end -->
|
|
243
243
|
|
package/bin/leerness.js
CHANGED
|
@@ -33,7 +33,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
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
|
const { findCorruptedStateJson: _findCorruptedStateJson } = require('../lib/state-integrity'); // 1.36.1 (클린룸 리뷰 FN): .harness/*.json 상태 무결성 (audit/health/check 공유)
|
|
35
35
|
|
|
36
|
-
const VERSION = '1.36.
|
|
36
|
+
const VERSION = '1.36.20';
|
|
37
37
|
|
|
38
38
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
39
39
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -2611,6 +2611,8 @@ function envCmd(root, sub) {
|
|
|
2611
2611
|
}
|
|
2612
2612
|
}
|
|
2613
2613
|
}
|
|
2614
|
+
// 1.36.20 (codex fresh-QA #7): apply 실패(read-only EPERM 등)를 exit code 에 반영 — 종전엔 action:'failed' 를 기록만 하고 exit 0.
|
|
2615
|
+
if (apply && (result.applied || []).some(a => a.action === 'failed')) process.exitCode = 1;
|
|
2614
2616
|
if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
|
|
2615
2617
|
log(cy(`# leerness env encoding-check (1.9.241/242, UR-0014) — 셸 스크립트 인코딩 위험 감지`));
|
|
2616
2618
|
log('');
|
|
@@ -2963,6 +2965,13 @@ function _selfTestCases() {
|
|
|
2963
2965
|
const realDecl = p._parseContractSpec('function realFn(){}\n').declared.includes('realFn'); // 회귀: 펜스 밖 선언 유지
|
|
2964
2966
|
return bracket && dotStill && fenceExcluded && realDecl;
|
|
2965
2967
|
} },
|
|
2968
|
+
{ name: 'apply-before-output mutation 순서 (1.36.20, codex fresh-QA #7): reuse/release --apply 가 --json return 전 수행 + 실패 exit code — 소스가드', run: () => {
|
|
2969
|
+
const s = read(__filename);
|
|
2970
|
+
const reuseOk = s.includes('project: path.basename(root), found, applied, added') && s.includes("failJson(has('--json'), 'no_reuse_map'"); // reuse: apply 먼저 → json 에 applied/added
|
|
2971
|
+
const releaseOk = s.includes('deleteCount: toDelete.length, deleted, deleteFailed') && s.includes('if (deleteFailed > 0) process.exitCode = 1'); // release: 삭제 먼저 → json 에 deleted
|
|
2972
|
+
const encOk = s.includes("(result.applied || []).some(a => a.action === 'failed')) process.exitCode = 1"); // encoding: 실패 exit1
|
|
2973
|
+
return reuseOk && releaseOk && encOk;
|
|
2974
|
+
} },
|
|
2966
2975
|
{ name: 'ID 리더 5자리+ 대응 (1.36.18, UR-0052 P1-2): \\d{4}→\\d{4,} — 10k+ ID(T-10000) truncation 방지, 날짜 연도는 exactly-4 보존 — 행위검사', run: () => {
|
|
2967
2976
|
// 행위: 5자리 ID 전체 캡처(구 \d{4}는 앞 4자리만 → truncation/충돌), 4자리 ID 무회귀, 날짜 연도 exactly-4 유지.
|
|
2968
2977
|
const taskRe = /\bT-(\d{4,})\b/;
|
|
@@ -15014,11 +15023,21 @@ function releaseCleanupCmd(root) {
|
|
|
15014
15023
|
});
|
|
15015
15024
|
const recent = mergedBranches.slice(0, keep);
|
|
15016
15025
|
const toDelete = mergedBranches.slice(keep).filter(b => b !== currentBranch);
|
|
15026
|
+
// 1.36.20 (codex fresh-QA #7): 삭제(mutation)를 출력(json/human) "전"에 수행. 종전엔 --json 분기가 삭제 전 return 해
|
|
15027
|
+
// `release cleanup --apply --json` 이 apply:true·deleteCount N 을 보고하고도 실제로 아무것도 안 지우던 silent no-op.
|
|
15028
|
+
let deleted = 0, deleteFailed = 0;
|
|
15029
|
+
if (apply) {
|
|
15030
|
+
for (const b of toDelete) {
|
|
15031
|
+
const r = cp.spawnSync('git', ['branch', '-d', b], { cwd: root, encoding: 'utf8' });
|
|
15032
|
+
if (r.status === 0) deleted++; else deleteFailed++;
|
|
15033
|
+
}
|
|
15034
|
+
if (deleteFailed > 0) process.exitCode = 1; // 실패 시 nonzero exit
|
|
15035
|
+
}
|
|
15017
15036
|
// 5) JSON
|
|
15018
15037
|
if (has('--json')) {
|
|
15019
15038
|
log(JSON.stringify({
|
|
15020
15039
|
apply, keep, total: allBranches.length, merged: mergedBranches.length,
|
|
15021
|
-
unmerged: unmergedBranches.length, deleteCount: toDelete.length,
|
|
15040
|
+
unmerged: unmergedBranches.length, deleteCount: toDelete.length, deleted, deleteFailed,
|
|
15022
15041
|
toDelete, recent: recent.slice(0, keep), unmergedSample: unmergedBranches.slice(0, 5)
|
|
15023
15042
|
}, null, 2));
|
|
15024
15043
|
return;
|
|
@@ -15049,13 +15068,7 @@ function releaseCleanupCmd(root) {
|
|
|
15049
15068
|
if (recent.length > 5) log(dm(` ... +${recent.length - 5}개`));
|
|
15050
15069
|
log('');
|
|
15051
15070
|
if (apply) {
|
|
15052
|
-
|
|
15053
|
-
for (const b of toDelete) {
|
|
15054
|
-
const r = cp.spawnSync('git', ['branch', '-d', b], { cwd: root, encoding: 'utf8' });
|
|
15055
|
-
if (r.status === 0) ok++;
|
|
15056
|
-
else fail++;
|
|
15057
|
-
}
|
|
15058
|
-
log(gr(` ✓ 삭제 완료: ${ok}/${toDelete.length}건`) + (fail > 0 ? rd(` · 실패 ${fail}`) : ''));
|
|
15071
|
+
log(gr(` ✓ 삭제 완료: ${deleted}/${toDelete.length}건`) + (deleteFailed > 0 ? rd(` · 실패 ${deleteFailed}`) : '')); // 1.36.20: 삭제는 출력 전 수행됨(#7)
|
|
15059
15072
|
} else {
|
|
15060
15073
|
log(dm(` → 적용: leerness release cleanup --apply (안전: merged만 삭제, 현재 branch 보호)`));
|
|
15061
15074
|
log(dm(` → --keep N: 최근 N개 유지 (default 5)`));
|
|
@@ -20304,37 +20317,39 @@ function reuseAutodetectCmd(root) {
|
|
|
20304
20317
|
}
|
|
20305
20318
|
}
|
|
20306
20319
|
}
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20310
|
-
|
|
20311
|
-
log(`# leerness reuse autodetect (1.9.35)`);
|
|
20312
|
-
log(`project: ${path.basename(root)}`);
|
|
20313
|
-
log(`발견된 capability 후보: ${found.length}건`);
|
|
20314
|
-
log('');
|
|
20315
|
-
log('| Capability | Where | Kind | Note |');
|
|
20316
|
-
log('|---|---|---|---|');
|
|
20317
|
-
for (const c of found) log(`| ${c.name} | ${c.file} | util | (autodetect from module.exports) |`);
|
|
20318
|
-
log('');
|
|
20320
|
+
// 1.36.20 (codex fresh-QA #7): --apply mutation 을 출력(human/json) "전"에 수행. 종전엔 --json 분기가 --apply 앞에서
|
|
20321
|
+
// return 해 `reuse autodetect --apply --json` 이 후보만 보고하고 실제 reuse-map 을 안 쓰던 silent no-op. 이제 먼저 mutate →
|
|
20322
|
+
// applied/added 를 human/json 양쪽에 반영. 에러도 --json 시 구조화(failJson).
|
|
20323
|
+
let applied = false, added = 0;
|
|
20319
20324
|
if (has('--apply')) {
|
|
20320
|
-
// reuse-map.md에 추가 (헤더 보존 + 후보 라인 append)
|
|
20321
20325
|
const reusePath = path.join(root, '.harness', 'reuse-map.md');
|
|
20322
20326
|
if (!exists(reusePath)) {
|
|
20323
|
-
|
|
20324
|
-
return process.exit(1);
|
|
20327
|
+
failJson(has('--json'), 'no_reuse_map', `.harness/reuse-map.md 없음 — leerness init 먼저 실행`);
|
|
20328
|
+
return process.exit(process.exitCode || 1);
|
|
20325
20329
|
}
|
|
20326
20330
|
let body = read(reusePath);
|
|
20327
|
-
let added = 0;
|
|
20328
20331
|
for (const c of found) {
|
|
20329
20332
|
if (body.includes(`| ${c.name} |`)) continue; // 이미 있음
|
|
20330
20333
|
body += `| ${c.name} | ${c.file} | util | autodetect 1.9.35 |\n`;
|
|
20331
20334
|
added++;
|
|
20332
20335
|
}
|
|
20333
20336
|
writeUtf8(reusePath, body);
|
|
20334
|
-
|
|
20335
|
-
}
|
|
20336
|
-
|
|
20337
|
+
applied = true;
|
|
20338
|
+
}
|
|
20339
|
+
if (has('--json')) {
|
|
20340
|
+
log(JSON.stringify({ project: path.basename(root), found, applied, added }, null, 2));
|
|
20341
|
+
return;
|
|
20337
20342
|
}
|
|
20343
|
+
log(`# leerness reuse autodetect (1.9.35)`);
|
|
20344
|
+
log(`project: ${path.basename(root)}`);
|
|
20345
|
+
log(`발견된 capability 후보: ${found.length}건`);
|
|
20346
|
+
log('');
|
|
20347
|
+
log('| Capability | Where | Kind | Note |');
|
|
20348
|
+
log('|---|---|---|---|');
|
|
20349
|
+
for (const c of found) log(`| ${c.name} | ${c.file} | util | (autodetect from module.exports) |`);
|
|
20350
|
+
log('');
|
|
20351
|
+
if (applied) log(`✓ ${added}건 reuse-map.md에 추가됨`);
|
|
20352
|
+
else log(`(--apply 로 reuse-map.md에 자동 추가)`);
|
|
20338
20353
|
}
|
|
20339
20354
|
|
|
20340
20355
|
// 1.9.165: leerness web — playwright bridge MVP (opt-in 의존성, 5능력 #1 보강)
|