leerness 1.36.10 → 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 CHANGED
@@ -1,5 +1,16 @@
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
+
3
14
  ## 1.36.10 — 2026-07-09 — FILE_RE Windows 백슬래시 경로 지원 (마지막 선재 백로그 소진)
4
15
 
5
16
  **verify-claim 의 주장-파일 추출이 Windows 백슬래시 경로를 잘라먹던 선재 결함(전 확장자 공통, codex 6차 core-diff 리뷰에서 지적 후 backlog).**
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.10 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
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.10는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
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.10는 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.10)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
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.10: 2026-07-10
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.10';
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') 시 호스트 프로세스 오염.
@@ -4032,6 +4032,26 @@ function _selfTestCases() {
4032
4032
  return extractedOk;
4033
4033
  } finally { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ } }
4034
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
+ } },
4035
4055
  { name: 'CLI 영어화 Phase 1 (1.20.2, UR-0010): _uiLang 해석(flag>env>manifest>ko) + 첫화면 _t 적용 (행위+소스)', run: () => {
4036
4056
  const save = process.argv; const saveEnv = process.env.LEERNESS_LANG;
4037
4057
  try {
@@ -4951,9 +4971,14 @@ function _lensDomainsForFiles(files) {
4951
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;
4952
4972
  // 단일 SQL 키워드는 산문 FP 위험(select/update/from/where 는 영어 단어) → 산문에 거의 없는 조합만: DDL/트랜잭션 토큰 또는
4953
4973
  // 완결형 문장 패턴(SELECT…FROM…WHERE / UPDATE…SET…WHERE / DELETE FROM…WHERE). 근접 조합이라 산문 FP 가 급감한다.
4954
- const _DB_SQL_RE = /\b(?:CREATE\s+TABLE|INSERT\s+INTO|ON\s+CONFLICT|BEGIN\s+(?:IMMEDIATE|TRANSACTION|DEFERRED|EXCLUSIVE)|FOR\s+UPDATE)\b|\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/i;
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;
4955
4980
  function _isDbContentText(txt) {
4956
- 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));
4957
4982
  }
4958
4983
  // claimed 파일 중 코드 파일을 읽어 DB 내용 신호가 있으면 true. 미존재/큰 파일(>512KB)은 건너뜀(안전·비용).
4959
4984
  // 보안(codex P2): 경로는 root 안으로 제한 — 절대경로/../ 트래버설로 워크스페이스 밖 파일을 읽지 않는다. 심볼릭 링크는 스킵.
@@ -10907,7 +10932,14 @@ function verifyClaimCmd(root, taskId, opts = {}) {
10907
10932
  // 'db\\migrations\\001_create.sql' 이 'create.sql' 로 잘려 추출돼 존재 검사가 false-fail 하던 결함.
10908
10933
  // 구분자에 \\ 허용 + 추출 후 / 로 정규화(하류의 git diff 비교·렌즈 매핑은 / 기준).
10909
10934
  const FILE_RE = new RegExp(`(?:[A-Za-z][A-Za-z0-9_-]*[\\/\\\\])?[A-Za-z][\\w./\\\\-]*\\.(?:${FILE_EXTS})\\b`, 'g');
10910
- const filePatterns = (evidence.match(FILE_RE) || []).map(f => f.replace(/\\/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
+ }
10911
10943
  // 중복 제거 + "tests/test.js" 같은 결과를 유지 (이미 `..` 없으니 그대로)
10912
10944
  const files = Array.from(new Set(filePatterns));
10913
10945
  // 1.9.20: 테스트 수 파싱 확장 — 한국어 + jest/mocha/tap/vitest
@@ -11258,7 +11290,9 @@ function verifyClaimCmd(root, taskId, opts = {}) {
11258
11290
  log(t(` 🧭 품질 렌즈 (완료 선언 전 자문 — advisory, 게이트 아님):`, ` 🧭 Quality lens (self-ask before declaring done — advisory, not a gate):`));
11259
11291
  for (const d of _lensDoms) {
11260
11292
  const l = _lensCat[d];
11261
- if (l) log(` · ${d}(${l.title}): ${l.questions[0]}`);
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]}`);
11262
11296
  }
11263
11297
  log(` ${t('→ 전체 질문', '→ full questions')}: leerness lens ${_lensDoms[0]}`);
11264
11298
  }
@@ -11655,7 +11689,9 @@ function depsImpactCmd(root, targetCapability) {
11655
11689
  process.argv.push('--all-apps');
11656
11690
  }
11657
11691
  const allPaths = _collectWorkspacePaths(root);
11658
- if (!allPaths.length) return fail('워크스페이스 프로젝트 없음. _apps/* 또는 --include 사용.');
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 사용.');
11659
11695
 
11660
11696
  // 1) 모든 reuse-map에서 entries + depends-on 엣지 수집
11661
11697
  const allEntries = []; // { project, entry }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.10",
3
+ "version": "1.36.11",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",