leerness 1.36.51 → 1.36.52

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.52 — 2026-07-21 — 외부 GPT 감사(1.36.47) P0 채택 (UR-0063)
4
+
5
+ 웹 구독 GPT 의 독립 감사(격리 설치·오류 주입·패치 첨부, 10건) 중 재현 확정 2건 + 채택 판단 2건을 반영. 전 건 현행 소스에서 독립 재현/검증 후 수정.
6
+
7
+ - **F-01 (재현확정) — 재-init 언어 뒤집힘**: en 프로젝트 init 2회차에 1회차가 생성한 한국어 관리 블록을 콘텐츠 감지가 오인해 ko 로 전환되던 멱등성 버그. 저장된 manifest/LANGUAGE 언어 최우선, 콘텐츠 감지는 신규만 (감사 첨부 패치 검증 후 채택). 실측: en→en 유지, 명시 --language ko 는 전환.
8
+ - **F-07 (재현확정) — memory restore 단수형 실패**: 생성 문서 예시(decision)와 CLI(decisions) 불일치 — 단수형 별칭 수용.
9
+ - **F-02 — 비율형 증거 우회**: "5/5 passed"(비율형 완전-통과)가 명시 카운트("5 tests passed")와 달리 정적 선언-수 대조를 우회하던 것 — 동일 정규화(1.35.9 원칙 유지: N/N 만 승격). verify-claim --json 에 신뢰 경계 명시: `semanticVerified:false` + `evidenceLevel: static|executed`.
10
+ - **F-08 — .env 권한**: Unix 에서 생성/병합 시 0600 (gitignore 는 커밋만 막고 로컬 노출은 못 막음).
11
+ - 감사 나머지: F-03(enforce=사용 게이트, status 한계 명시 기존) / F-06(전용 스캐너 병행 — 1.35.8 문서 기존) 은 방향 일치 확인, F-04(managed 무결성 복구)/F-05(en 완전 번역)/F-09(MCP 프로필)/E2E 아티팩트 이식성은 백로그 편입.
12
+ - 검증: selftest 326(+1 통합 행위), e2e.
13
+
3
14
  ## 1.36.51 — 2026-07-21 — 모호성 질문 + 미리보기 승인 워크플로 (사용자 요청 UR-0061)
4
15
 
5
16
  사용자 요청: "자연어에 판단 모호한 부분이 있으면 사용자에게 질문하고, 신규 기능은 코드 작성 전 디자인/기능 미리보기를 제시해 승인/수정을 받도록".
package/README.md CHANGED
@@ -122,7 +122,7 @@ MIT
122
122
  <!-- leerness:project-readme:start -->
123
123
  ## Leerness Project Harness
124
124
 
125
- 이 프로젝트는 Leerness v1.36.51 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
125
+ 이 프로젝트는 Leerness v1.36.52 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
126
126
 
127
127
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
128
128
 
@@ -176,7 +176,7 @@ leerness memory restore decision <date|title>
176
176
 
177
177
  ### MCP server (외부 AI 통합)
178
178
 
179
- Leerness v1.36.51는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
179
+ Leerness v1.36.52는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
180
180
 
181
181
  ```jsonc
182
182
  // 카테고리별
@@ -197,7 +197,7 @@ Leerness v1.36.51는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
197
197
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
198
198
  1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) 회귀 테스트 갱신 → 4) 전체 e2e 스위트 통과 → 5) npm publish + git tag → 6) main push → 7) session close → 8) 다음 라운드 예약.
199
199
 
200
- 현재 누적: **v1.9.x → 1.36.51 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
200
+ 현재 누적: **v1.9.x → 1.36.52 릴리스 태그 이력** (수백 라운드) · _reports/는 비공개 보존.
201
201
 
202
202
  ### 성능 가이드
203
203
 
@@ -235,6 +235,6 @@ leerness release pack --close --auto-main-push
235
235
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
236
236
  - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
237
237
 
238
- Last synced by Leerness v1.36.51: 2026-07-21
238
+ Last synced by Leerness v1.36.52: 2026-07-21
239
239
  <!-- leerness:project-readme:end -->
240
240
 
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.51';
37
+ const VERSION = '1.36.52';
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') 시 호스트 프로세스 오염.
@@ -240,6 +240,21 @@ function detectProjectName(root) { try { const pkg = JSON.parse(read(path.join(r
240
240
  function detectLanguageValue(root, value = 'auto') {
241
241
  const v = String(value || 'auto').toLowerCase();
242
242
  if (v === 'ko' || v === 'en') return v;
243
+ // 1.36.52 (외부 GPT 감사 F-01, 재현확정): 이미 초기화된 프로젝트는 저장된 명시 언어를 최우선 —
244
+ // 1회차 init 이 생성한 한국어 관리 블록을 2회차 콘텐츠 감지가 오인해 en→ko 로 뒤집던 멱등성 버그.
245
+ // 콘텐츠 자동 감지는 신규 프로젝트에만. (감사 첨부 패치 검증 후 채택)
246
+ try {
247
+ const mf = path.join(root, '.harness', 'manifest.json');
248
+ if (exists(mf)) {
249
+ const saved = String((JSON.parse(read(mf)) || {}).language || '').toLowerCase();
250
+ if (saved === 'ko' || saved === 'en') return saved;
251
+ }
252
+ const lf = path.join(root, '.harness', 'LANGUAGE');
253
+ if (exists(lf)) {
254
+ const saved = String(read(lf) || '').trim().toLowerCase();
255
+ if (saved === 'ko' || saved === 'en') return saved;
256
+ }
257
+ } catch {}
243
258
  // ① 프로젝트 콘텐츠 한글 우선 — 콘텐츠가 있으면 그 의도를 존중 (기존 동작 보존).
244
259
  const candidates = ['README.md', 'docs/guideline.md', '.harness/project-brief.md', '.harness/plan.md'];
245
260
  let text = '';
@@ -744,6 +759,8 @@ function mergeLinesFile(p, lines) {
744
759
  function mergeEnvFile(p, lines) {
745
760
  const current = exists(p) ? read(p) : '';
746
761
  writeUtf8(p, _mergeEnvLines(current, lines));
762
+ // 1.36.52 (외부 GPT 감사 F-08): Unix 에서 .env 는 소유자 전용(0600) — gitignore 는 커밋만 막지 로컬 노출은 못 막는다.
763
+ if (process.platform !== 'win32') { try { fs.chmodSync(p, 0o600); } catch {} }
747
764
  }
748
765
 
749
766
  function writeMigrationReport(root, backup, actions, opts = {}) {
@@ -5019,6 +5036,26 @@ function _selfTestCases() {
5019
5036
  const preserved = fs.readFileSync(c._previewsPath(tmp), 'utf8') === '[{"id":"P-0001",';
5020
5037
  return added && pending1 && revised && approved && preserved;
5021
5038
  } finally { process.stdout.write = _w; console.error = _ce; try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} }
5039
+ } },
5040
+ { name: '외부 GPT 감사 P0 (1.36.52, UR-0063): 재init 언어보존(F-01) + restore 별칭(F-07) + ratio 증거 정규화/evidenceLevel(F-02) + .env 0600(F-08) — 행위+소스가드', run: () => {
5041
+ // F-01: manifest 언어가 콘텐츠 감지보다 우선 (행위)
5042
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), '__leerness_lang_'));
5043
+ let langOk = false;
5044
+ try {
5045
+ fs.mkdirSync(path.join(tmp, '.harness'), { recursive: true });
5046
+ fs.writeFileSync(path.join(tmp, '.harness', 'manifest.json'), '{"language":"en"}');
5047
+ fs.writeFileSync(path.join(tmp, 'README.md'), '# 한국어 콘텐츠 가득한 리드미'); // 종전엔 이것이 ko 로 뒤집었다
5048
+ langOk = detectLanguageValue(tmp, 'auto') === 'en' && detectLanguageValue(tmp, 'ko') === 'ko';
5049
+ } finally { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} }
5050
+ const s = read(__filename);
5051
+ // F-07: 단수형 별칭 매핑 존재
5052
+ const aliasOk = s.includes("({ decision: 'decisions', lesson: 'lessons', plans: 'plan' })[surface]");
5053
+ // F-02: 비율형 완전-통과 → declaredTestCount 승격 + 신뢰 경계 필드
5054
+ const ratioOk = s.includes('declaredPass.num === declaredPass.denom) declaredTestCount = declaredPass.denom')
5055
+ && s.includes('semanticVerified: false') && s.includes("? 'executed' : 'static'");
5056
+ // F-08: Unix .env 0600
5057
+ const envOk = /mergeEnvFile[\s\S]{0,600}chmodSync\(p, 0o600\)/.test(s);
5058
+ return langOk && aliasOk && ratioOk && envOk;
5022
5059
  } }
5023
5060
  ];
5024
5061
  }
@@ -9129,6 +9166,8 @@ function memoryArchiveListCmd(root, opts = {}) {
9129
9166
  // 매칭 archive 블록을 active 파일 끝에 추가 + archive 에서 제거
9130
9167
  function memoryRestoreCmd(root, surface, target) {
9131
9168
  root = absRoot(root);
9169
+ // 1.36.52 (외부 GPT 감사 F-07): 단수형 별칭 수용 — 생성 문서 예시(decision)와 CLI(decisions) 불일치로 실패하던 것.
9170
+ surface = ({ decision: 'decisions', lesson: 'lessons', plans: 'plan' })[surface] || surface;
9132
9171
  if (!surface || !['decisions', 'lessons', 'plan'].includes(surface)) {
9133
9172
  return fail('memory restore <decisions|lessons|plan> <target> 필요 (target: date YYYY-MM-DD 또는 substring)');
9134
9173
  }
@@ -12180,6 +12219,11 @@ function verifyClaimCmd(root, taskId, opts = {}) {
12180
12219
  const m5 = evidence.match(/(\d+)\s*tests?\b/i);
12181
12220
  if (m5) declaredTestCount = parseInt(m5[1], 10);
12182
12221
  }
12222
+ // 1.36.52 (외부 GPT 감사 F-02, 재현확정): 비율형 완전-통과("5/5 passed")를 명시 카운트와 동일 정규화 —
12223
+ // 종전엔 declaredTestCount 로만 정적 선언-수 대조를 해서 비율형 주장이 그 게이트를 우회했다.
12224
+ // 1.35.9 원칙 유지: 전부-통과(N/N) 주장만 승격(부분 비율은 비-테스트 서술일 수 있어 미승격).
12225
+ let _ratioPromoted = false;
12226
+ if (declaredTestCount == null && declaredPass && declaredPass.num === declaredPass.denom) { declaredTestCount = declaredPass.denom; _ratioPromoted = true; }
12183
12227
 
12184
12228
  // 실제 파일 존재 검사
12185
12229
  const fileChecks = files.map(f => ({ file: f, exists: exists(path.join(root, f)) }));
@@ -12286,6 +12330,10 @@ function verifyClaimCmd(root, taskId, opts = {}) {
12286
12330
  // 1.9.309 (UR-0048, 설치리뷰 Opus critical): done 주장은 evidence(파일+테스트) 기본 강제 — 거짓완료 차단을 기본값·MCP·json 모두에서 작동.
12287
12331
  // 이전: --require-evidence 플래그 시에만 검사 → 증거0 done 이 기본 통과(fileChecks.every([])=공허참)했고 MCP 는 도달 불가했음.
12288
12332
  // 이제 done/완료 주장은 기본 강제. opt-out: --lenient. 비-done 주장은 영향 없음.
12333
+ // 1.36.52 (F-02 보정, 게이트 실측): 비율형 승격은 "정적 검증 전용" — --run-tests 가 주장 수 이상 전부-통과를
12334
+ // 실제 실행으로 확증하면 정적 선언-수 대조를 재적용하지 않는다(실행 증거 > 정적 휴리스틱; jest/mocha 러너는
12335
+ // 선언을 소스-grep 으로 셀 수 없는 형식이 많아 실행 확증 케이스를 정적 게이트가 거짓 차단했다).
12336
+ if (_ratioPromoted && runResult && !runResult.skipped && runResult.allPassed && runResult.parsed && runResult.parsed.num >= declaredTestCount) declaredTestCount = null;
12289
12337
  const isDoneClaim = /done|완료|completed/i.test(row.status || '');
12290
12338
  const evq = _evidenceQuality(evidence);
12291
12339
  const mustHaveEvidence = !has('--lenient') && (isDoneClaim || has('--require-evidence'));
@@ -12343,6 +12391,10 @@ function verifyClaimCmd(root, taskId, opts = {}) {
12343
12391
  const out = {
12344
12392
  project: path.basename(root),
12345
12393
  taskId, row,
12394
+ // 1.36.52 (외부 GPT 감사 F-02): 신뢰 경계 명시 — 이 검증은 정적/실행 흔적 대조이지 의미론 보증이 아니다.
12395
+ // evidenceLevel: static(파일·문자열 대조만) | executed(--run-tests 실제 실행 포함). 소비자가 경계를 오인하지 않도록 스키마에 고정.
12396
+ semanticVerified: false,
12397
+ evidenceLevel: (runResult && !runResult.skipped) ? 'executed' : 'static',
12346
12398
  declared: { files: files.length, pass: declaredPass, testCount: declaredTestCount },
12347
12399
  actual: { fileChecks, testCount: actualTestCount },
12348
12400
  verdict: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.51",
3
+ "version": "1.36.52",
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",