leerness 1.36.9 → 1.36.11
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 +19 -0
- package/README.md +4 -4
- package/bin/leerness.js +61 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.36.11 — 2026-07-09 — 17th 클린룸 리뷰 4/4 채택: deps --json 누수 + UNC 우회 + DB렌즈 prose FP + en 렌즈 i18n
|
|
4
|
+
|
|
5
|
+
**게시본 1.36.10 을 codex 가 클린룸(fresh npm install)에서 행위로만 적대 검증(이 세션 7개 신규 표면) — P1×2, P2×2 전부 명령+출력 증거로 확정, 4/4 수정.**
|
|
6
|
+
|
|
7
|
+
- **P1-A `deps --json` plain-text 누수** — 워크스페이스-없음 에러 경로가 failJson 미배선 → **설치본 selftest 283/284 실패**의 원인. dev 트리는 `_apps` 가 있어 이 분기를 안 타 게이트가 못 잡는 **환경 의존 경로**였음(클린룸 리뷰의 존재 이유를 재확인). → `no_workspace_projects` 구조화.
|
|
8
|
+
- **P1-B UNC 접두 조용한 변환** — evidence `\\src\thing.mjs` 의 UNC 접두를 벗겨 무관한 로컬 `src/thing.mjs` 를 검증(존재검사 우회성). → 매치 직전이 경로 구분자면 "더 긴 경로의 꼬리"로 보고 추출 제외(드라이브 `C:\...`·URL 꼬리도 함께 차단). `파일:src/x.js` 정상 케이스는 유지.
|
|
9
|
+
- **P2-A DB 렌즈 prose FP** — `// select from the menu where …`(주석)·JSX 텍스트가 렌즈를 발화. → 완결형 SQL 문장은 **대문자 키워드(case-sensitive) 또는 따옴표/백틱 문맥**일 때만 인정 — 실제 코드 SQL 은 거의 항상 둘 중 하나라 recall 손실 없음(인용 소문자·대문자 SQL recall 행위 검증).
|
|
10
|
+
- **P2-B en 로케일 렌즈 한국어 누출** — verify-claim 렌즈 블록이 titleEn/questionsEn 미사용. → lensCmd 와 동일 규칙 적용.
|
|
11
|
+
- codex 검증 통과 표면(수정 불요): handoff 넛지(부패 JSON·미래 타임스탬프·10만 history·--json 무오염·opt-out 전부 견딤), optimism [DB] 수정(빈 .sql 경계 포함), 정직화 문구 4종 잔존 과장 없음.
|
|
12
|
+
- selftest 285 (신규: 4수정 행위+소스 가드) + e2e 통과.
|
|
13
|
+
|
|
14
|
+
## 1.36.10 — 2026-07-09 — FILE_RE Windows 백슬래시 경로 지원 (마지막 선재 백로그 소진)
|
|
15
|
+
|
|
16
|
+
**verify-claim 의 주장-파일 추출이 Windows 백슬래시 경로를 잘라먹던 선재 결함(전 확장자 공통, codex 6차 core-diff 리뷰에서 지적 후 backlog).**
|
|
17
|
+
|
|
18
|
+
- 증상(충실 재현): evidence `src\thing.mjs` → `thing.mjs` 로(디렉터리 소실 → 존재 검사 false-fail), `db\migrations\001_create.sql` → `create.sql` 로(숫자 시작 세그먼트까지 소실) 추출.
|
|
19
|
+
- 수정: `FILE_RE` 구분자에 `\\` 허용 + 추출 직후 `/` 정규화(하류의 git diff 비교·렌즈 도메인 매핑은 `/` 기준이라 일관).
|
|
20
|
+
- 검증: 백슬래시/다단/혼합 6케이스 전부 정확 추출·정규화, forward-슬래시 회귀 0, 버전 문자열(`1.36.9`)·확장자 없는 경로(`C:\Users\…`) FP 0. selftest 284 (신규 행위 케이스: 백슬래시 evidence done 주장이 files-missing 없이 통과) + e2e 통과.
|
|
21
|
+
|
|
3
22
|
## 1.36.9 — 2026-07-09 — optimism [DB] FP 수정: SQL-migration 프로젝트의 정직한 done 주장 무오탐 (dogfood 백로그)
|
|
4
23
|
|
|
5
24
|
**stock-service 도그푸드에서 두 번 마주친 선재 FP 소진(깨끗한 1.36.2에서도 동일 재현 확인 — 이번 세션 회귀 아님).**
|
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.11 하네스를 사용합니다. 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.11는 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.9는 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.36.
|
|
203
|
+
현재 누적: **70 라운드 (1.9.40 → 1.36.11)** · 매 라운드 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.11: 2026-07-10
|
|
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.11';
|
|
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') 시 호스트 프로세스 오염.
|
|
@@ -4017,6 +4017,41 @@ function _selfTestCases() {
|
|
|
4017
4017
|
return scannedOk && noFp && stillDetects;
|
|
4018
4018
|
} finally { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ } }
|
|
4019
4019
|
} },
|
|
4020
|
+
{ name: 'FILE_RE 백슬래시 경로 (1.36.10, 선재 백로그): Windows 경로 추출+정규화 · forward 회귀 0 · FP 0 (행위)', run: () => {
|
|
4021
|
+
const os = require('os');
|
|
4022
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lz-bs-'));
|
|
4023
|
+
const hd = path.join(dir, '.harness'); fs.mkdirSync(hd);
|
|
4024
|
+
fs.mkdirSync(path.join(dir, 'src'));
|
|
4025
|
+
fs.writeFileSync(path.join(dir, 'src', 'thing.mjs'), 'export const x = 1;\n// real impl with enough content to not be a stub\nexport function go(){ return x + 1; }\n');
|
|
4026
|
+
try {
|
|
4027
|
+
// 백슬래시 evidence 의 done 주장 → 파일 존재 검사가 통과해야 함(이전엔 디렉터리 소실로 false-fail)
|
|
4028
|
+
fs.writeFileSync(path.join(hd, 'progress-tracker.md'),
|
|
4029
|
+
'| ID | Status | Request | Evidence | Next Action | Updated |\n|---|---|---|---|---|---|\n| T-0001 | done | 작업 | src\\thing.mjs 수정 완료 | - | 2026-07-09 |\n');
|
|
4030
|
+
const r = verifyClaimCmd(dir, 'T-0001', { collect: true });
|
|
4031
|
+
const extractedOk = r && Array.isArray(r.reasons) && !r.reasons.includes('files-missing');
|
|
4032
|
+
return extractedOk;
|
|
4033
|
+
} finally { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ } }
|
|
4034
|
+
} },
|
|
4035
|
+
{ name: '17th 클린룸 리뷰 (1.36.11): deps --json failJson + UNC 꼬리매치 스킵 + DB렌즈 prose FP 제거 + en 렌즈 i18n (행위+소스)', run: () => {
|
|
4036
|
+
const self = read(__filename);
|
|
4037
|
+
// P1-A: deps 워크스페이스-없음 경로 failJson 배선 (소스 가드)
|
|
4038
|
+
const depsJsonOk = self.includes("failJson(has('--json'), 'no_workspace_projects'");
|
|
4039
|
+
// P1-B: UNC/꼬리 매치 스킵 — \\src\thing.mjs 는 추출 안 됨(무관 로컬 경로 검증 차단), 정상 백슬래시는 유지 (행위)
|
|
4040
|
+
const ev = String.fromCharCode(92) + String.fromCharCode(92) + 'src' + String.fromCharCode(92) + 'thing.mjs 그리고 src' + String.fromCharCode(92) + 'other.mjs 수정';
|
|
4041
|
+
const FE = 'mjs|js';
|
|
4042
|
+
const FR = new RegExp(`(?:[A-Za-z][A-Za-z0-9_-]*[\\/\\\\])?[A-Za-z][\\w./\\\\-]*\\.(?:${FE})\\b`, 'g');
|
|
4043
|
+
const got = [];
|
|
4044
|
+
for (const m of ev.matchAll(FR)) { const p = m.index > 0 ? ev[m.index - 1] : ''; if (p === '\\' || p === '/') continue; got.push(m[0].replace(/\\/g, '/')); }
|
|
4045
|
+
const uncOk = JSON.stringify(got) === JSON.stringify(['src/other.mjs']);
|
|
4046
|
+
// P2-A: prose FP 제거 + recall 유지 (행위)
|
|
4047
|
+
const proseOk = !_isDbContentText('// select from the menu where appropriate')
|
|
4048
|
+
&& !_isDbContentText('<div>Select from the menu where needed</div>')
|
|
4049
|
+
&& _isDbContentText("db.prepare('select * from t where id = ?')") // 인용 소문자 SQL recall 유지
|
|
4050
|
+
&& _isDbContentText('SELECT id FROM users WHERE active = 1'); // 대문자 SQL recall 유지
|
|
4051
|
+
// P2-B: en 렌즈 i18n (소스 가드)
|
|
4052
|
+
const enLensOk = self.includes('(_en && l.questionsEn && l.questionsEn[0]) || l.questions[0]');
|
|
4053
|
+
return depsJsonOk && uncOk && proseOk && enLensOk;
|
|
4054
|
+
} },
|
|
4020
4055
|
{ name: 'CLI 영어화 Phase 1 (1.20.2, UR-0010): _uiLang 해석(flag>env>manifest>ko) + 첫화면 _t 적용 (행위+소스)', run: () => {
|
|
4021
4056
|
const save = process.argv; const saveEnv = process.env.LEERNESS_LANG;
|
|
4022
4057
|
try {
|
|
@@ -4936,9 +4971,14 @@ function _lensDomainsForFiles(files) {
|
|
|
4936
4971
|
const _DB_IMPORT_RE = /(?:require\s*\(|\bfrom|\bimport)\s*['"](?:node:sqlite|better-sqlite3|sqlite3|pg|postgres|mysql2?|knex|typeorm|sequelize|drizzle-orm|@prisma\/client|mongodb|mongoose|ioredis|redis|oracledb|tedious|@libsql\/client)['"]/i;
|
|
4937
4972
|
// 단일 SQL 키워드는 산문 FP 위험(select/update/from/where 는 영어 단어) → 산문에 거의 없는 조합만: DDL/트랜잭션 토큰 또는
|
|
4938
4973
|
// 완결형 문장 패턴(SELECT…FROM…WHERE / UPDATE…SET…WHERE / DELETE FROM…WHERE). 근접 조합이라 산문 FP 가 급감한다.
|
|
4939
|
-
|
|
4974
|
+
// 1.36.11 (클린룸 P2-A): 완결형 문장은 산문("// select from the menu where …", JSX 텍스트)에도 나올 수 있어
|
|
4975
|
+
// **대문자 키워드(case-sensitive)** 또는 **따옴표/백틱 문맥(인용 시작 40자 내)** 일 때만 인정 — 실제 코드의 SQL 은
|
|
4976
|
+
// 거의 항상 둘 중 하나(관례적 대문자 또는 문자열 리터럴)라 recall 손실 없이 prose FP 를 제거.
|
|
4977
|
+
const _DB_SQL_RE = /\b(?:CREATE\s+TABLE|INSERT\s+INTO|ON\s+CONFLICT|BEGIN\s+(?:IMMEDIATE|TRANSACTION|DEFERRED|EXCLUSIVE)|FOR\s+UPDATE)\b/i;
|
|
4978
|
+
const _DB_STMT_UPPER_RE = /\bSELECT\b[\s\S]{0,120}?\bFROM\b[\s\S]{0,120}?\bWHERE\b|\bUPDATE\b[\s\S]{0,80}?\bSET\b[\s\S]{0,200}?\bWHERE\b|\bDELETE\s+FROM\b[\s\S]{0,120}?\bWHERE\b/; // case-sensitive
|
|
4979
|
+
const _DB_STMT_QUOTED_RE = /['"`][^'"`\n]{0,40}?\bselect\b[\s\S]{0,120}?\bfrom\b[\s\S]{0,120}?\bwhere\b|['"`][^'"`\n]{0,40}?\bupdate\b[\s\S]{0,80}?\bset\b[\s\S]{0,200}?\bwhere\b|['"`][^'"`\n]{0,40}?\bdelete\s+from\b[\s\S]{0,120}?\bwhere\b/i;
|
|
4940
4980
|
function _isDbContentText(txt) {
|
|
4941
|
-
return typeof txt === 'string' && (_DB_IMPORT_RE.test(txt) || _DB_SQL_RE.test(txt));
|
|
4981
|
+
return typeof txt === 'string' && (_DB_IMPORT_RE.test(txt) || _DB_SQL_RE.test(txt) || _DB_STMT_UPPER_RE.test(txt) || _DB_STMT_QUOTED_RE.test(txt));
|
|
4942
4982
|
}
|
|
4943
4983
|
// claimed 파일 중 코드 파일을 읽어 DB 내용 신호가 있으면 true. 미존재/큰 파일(>512KB)은 건너뜀(안전·비용).
|
|
4944
4984
|
// 보안(codex P2): 경로는 root 안으로 제한 — 절대경로/../ 트래버설로 워크스페이스 밖 파일을 읽지 않는다. 심볼릭 링크는 스킵.
|
|
@@ -10888,8 +10928,18 @@ function verifyClaimCmd(root, taskId, opts = {}) {
|
|
|
10888
10928
|
// 1.9.21: 설정/메타 파일 확장자 추가 — Godot export_presets.cfg 등 false negative 보완
|
|
10889
10929
|
// 1.18.2: java|php|mjs|cjs 추가 — _VC_CODE_EXT 와 정합(이전엔 .java/.php 임플 주장이 추출조차 안 돼 스텁/존재 검사를 무검사 통과).
|
|
10890
10930
|
const FILE_EXTS = 'webmanifest|dockerfile|properties|tscn|tres|godot|json5|prisma|java|jsx|tsx|yaml|html|scss|sass|less|gltf|conf|json|toml|lock|mdx|xml|css|svg|yml|cfg|ini|env|php|sql|mjs|cjs|md|js|ts|gd|cs|py|rb|go|rs|kt|sh|h';
|
|
10891
|
-
|
|
10892
|
-
|
|
10931
|
+
// 1.36.10 (선재 백로그): Windows 백슬래시 경로 지원 — 'src\\inventory.mjs' 가 'inventory.mjs' 로,
|
|
10932
|
+
// 'db\\migrations\\001_create.sql' 이 'create.sql' 로 잘려 추출돼 존재 검사가 false-fail 하던 결함.
|
|
10933
|
+
// 구분자에 \\ 허용 + 추출 후 / 로 정규화(하류의 git diff 비교·렌즈 매핑은 / 기준).
|
|
10934
|
+
const FILE_RE = new RegExp(`(?:[A-Za-z][A-Za-z0-9_-]*[\\/\\\\])?[A-Za-z][\\w./\\\\-]*\\.(?:${FILE_EXTS})\\b`, 'g');
|
|
10935
|
+
// 1.36.11 (클린룸 P1-B): 매치 직전 문자가 경로 구분자면 더 긴 경로의 "꼬리"만 잡은 것(UNC \\\\server\\..., 드라이브 X:\\...,
|
|
10936
|
+
// 숫자 시작 선두 세그먼트 등) — 잘린 꼬리를 루트-상대 경로로 검증하면 무관한 로컬 파일이 통과(우회성). 그런 매치는 버린다.
|
|
10937
|
+
const filePatterns = [];
|
|
10938
|
+
for (const m of evidence.matchAll(FILE_RE)) {
|
|
10939
|
+
const prev = m.index > 0 ? evidence[m.index - 1] : '';
|
|
10940
|
+
if (prev === '\\' || prev === '/') continue; // suffix-of-longer-path(UNC \\srv\..., C:\..., URL) → skip. ':' 는 "파일:src/x.js" 정상 케이스라 스킵 안 함
|
|
10941
|
+
filePatterns.push(m[0].replace(/\\/g, '/'));
|
|
10942
|
+
}
|
|
10893
10943
|
// 중복 제거 + "tests/test.js" 같은 결과를 유지 (이미 `..` 없으니 그대로)
|
|
10894
10944
|
const files = Array.from(new Set(filePatterns));
|
|
10895
10945
|
// 1.9.20: 테스트 수 파싱 확장 — 한국어 + jest/mocha/tap/vitest
|
|
@@ -11240,7 +11290,9 @@ function verifyClaimCmd(root, taskId, opts = {}) {
|
|
|
11240
11290
|
log(t(` 🧭 품질 렌즈 (완료 선언 전 자문 — advisory, 게이트 아님):`, ` 🧭 Quality lens (self-ask before declaring done — advisory, not a gate):`));
|
|
11241
11291
|
for (const d of _lensDoms) {
|
|
11242
11292
|
const l = _lensCat[d];
|
|
11243
|
-
|
|
11293
|
+
// 1.36.11 (클린룸 P2-B): en 로케일에서 렌즈 제목/질문이 한국어로 새던 것 — titleEn/questionsEn 사용(lensCmd 와 동일 규칙).
|
|
11294
|
+
const _en = _uiLang(root) === 'en';
|
|
11295
|
+
if (l) log(` · ${d}(${(_en && l.titleEn) || l.title}): ${(_en && l.questionsEn && l.questionsEn[0]) || l.questions[0]}`);
|
|
11244
11296
|
}
|
|
11245
11297
|
log(` ${t('→ 전체 질문', '→ full questions')}: leerness lens ${_lensDoms[0]}`);
|
|
11246
11298
|
}
|
|
@@ -11637,7 +11689,9 @@ function depsImpactCmd(root, targetCapability) {
|
|
|
11637
11689
|
process.argv.push('--all-apps');
|
|
11638
11690
|
}
|
|
11639
11691
|
const allPaths = _collectWorkspacePaths(root);
|
|
11640
|
-
|
|
11692
|
+
// 1.36.11 (클린룸 P1-A): --json 인데 plain-text 로 새던 에러 경로 — failJson 배선 (설치본 selftest 283/284 의 원인:
|
|
11693
|
+
// dev 트리는 _apps 가 있어 이 분기를 안 타 게이트가 못 잡았음; 클린룸(워크스페이스 없음)에서만 노출되는 환경 의존 경로).
|
|
11694
|
+
if (!allPaths.length) return failJson(has('--json'), 'no_workspace_projects', '워크스페이스 프로젝트 없음. _apps/* 또는 --include 사용.');
|
|
11641
11695
|
|
|
11642
11696
|
// 1) 모든 reuse-map에서 entries + depends-on 엣지 수집
|
|
11643
11697
|
const allEntries = []; // { project, entry }
|