leerness 1.36.6 → 1.36.8

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,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.36.8 — 2026-07-09 — 정직성 marginal 소진 + 넛지 확장 (심층 감사 P3 완결)
4
+
5
+ **심층 정직성 감사(1.36.5)의 마지막 marginal P3 2건 소진 + handoff 넛지를 실사용 최다 명령으로 확장.**
6
+
7
+ - **encoding "passed" 스코프 명시** — `passed` 는 전체 트리가 아니라 allowlist 텍스트 확장자(≤5MB)만 검사한 결과 → `encoding check passed (scope: allowlisted text extensions, files ≤5MB)`.
8
+ - **"self-heal" 범위 정직화** — identity/about/README 의 `자동 감사 + self-heal` 이 secret 자동치유를 암시 → 실제 범위 명시: `self-heal: drift·idempotency --auto-fix, encoding --apply; secret 은 감지 전용`.
9
+ - **넛지 확장** — `task update`(작업 중 최다 실행 명령)에도 handoff 넛지 노출(1.36.7 add 계열과 동일 가드/opt-out).
10
+ - selftest 282 (신규 소스가드, split-literal self-ref 회피) + e2e 통과. 이로써 1.36.5 심층 감사가 확정한 20건 중 수정 대상 전부 소진(잔여는 과거 CHANGELOG/주석의 역사 기록 — 소급 수정 안 함).
11
+
12
+ ## 1.36.7 — 2026-07-09 — handoff 넛지: 세션-시작 리추얼 사각지대 메꿈 (메타-테스트 도그푸드)
13
+
14
+ **자기 행동 실측(세션 트랜스크립트 파싱)에서 드러난 사각지대를 leerness 기능으로 메꿈.** "세션이 길수록 leerness를 덜 참조하는가?" 가설을 트랜스크립트로 측정 → **반박**(workspace 사용은 세션 길이가 아니라 작업 종류에 종속). 단 실재 gap 1건: **compaction/resume 직후 세션-시작 `leerness handoff`를 건너뜀**(요약이 "resume mid-task"를 지시). 그 신호는 handoff 출력에만 떴는데 정작 handoff 를 안 돌리는 게 문제.
15
+
16
+ - 신규: `_handoffNudgeState`/`_emitHandoffNudge` — `task add`/`decision add`(에이전트가 작업 중 실제로 실행하는 명령) 시, 마지막 handoff 가 오래됨(gap > 120분)이면 **한 줄 넛지**: `⚠ 마지막 handoff N시간 전 — 'leerness handoff .' 권장`.
17
+ - FP 가드: **기록 있음 + gap > 임계**일 때만(신규 프로젝트=last-handoff.json 부재 → 넛지 안 함). `--json`/`--quiet` 오염 없음. opt-out `LEERNESS_NO_HANDOFF_NUDGE=1`.
18
+ - 정본 `_getLastHandoffGap` 재사용(스키마 일관). selftest 281(행위: 오래됨→넛지·최근/부재→없음·opt-out) + e2e 통과.
19
+
3
20
  ## 1.36.6 — 2026-07-09 — 정직성 백로그 소진: 강제력/종합-health 문구 정직화 (심층 감사 P2/P3 후속)
4
21
 
5
22
  **1.36.5 심층 정직성 감사가 확인했으나 미수정으로 남긴 P2/P3 강제력·과장 문구를 정직화.**
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.6 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
128
+ 이 프로젝트는 Leerness v1.36.8 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
129
129
 
130
130
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
131
131
 
@@ -135,7 +135,7 @@ Leerness 는 **실행기/코딩 에이전트가 아니라**, 어떤 AI 코딩
135
135
  - **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트
136
136
  - **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩
137
137
  - **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 감지 (권고; CI 게이트 필수화 시 차단)
138
- - **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 + self-heal
138
+ - **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 (self-heal: drift·idempotency --auto-fix, encoding --apply; secret 은 감지 전용)
139
139
 
140
140
  AGENTS.md(정적 지침)을 **대체하지 않고 보완**합니다 — 정적 규칙은 AGENTS.md, 동적 상태·검증·인수인계는 leerness. 정체성 조회: `leerness about` (MCP `leerness_about`).
141
141
 
@@ -179,7 +179,7 @@ leerness memory restore decision <date|title>
179
179
 
180
180
  ### MCP server (외부 AI 통합)
181
181
 
182
- Leerness v1.36.6는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
182
+ Leerness v1.36.8는 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.6는 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.6)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
203
+ 현재 누적: **70 라운드 (1.9.40 → 1.36.8)** · 매 라운드 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.6: 2026-07-09
241
+ Last synced by Leerness v1.36.8: 2026-07-09
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.6';
36
+ const VERSION = '1.36.8';
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') 시 호스트 프로세스 오염.
@@ -302,7 +302,7 @@ function managedReadmeBlock(project) {
302
302
  '- **정책(Policy)** — 8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트',
303
303
  '- **인수인계(Handoff)** — 에이전트 간 컨텍스트 표준 전달 + `get_project_context` 1콜 온보딩',
304
304
  '- **검증(Verification)** — 근거 기반 완료 검증으로 허위 완료 감지 (권고; CI 게이트 필수화 시 차단)',
305
- '- **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 + self-heal',
305
+ '- **감사(Audit)** — drift/idempotency/secret/encoding 자동 감사 (self-heal: drift·idempotency --auto-fix, encoding --apply; secret 은 감지 전용)',
306
306
  '',
307
307
  'AGENTS.md(정적 지침)을 **대체하지 않고 보완**합니다 — 정적 규칙은 AGENTS.md, 동적 상태·검증·인수인계는 leerness. 정체성 조회: `leerness about` (MCP `leerness_about`).',
308
308
  '',
@@ -3973,6 +3973,35 @@ function _selfTestCases() {
3973
3973
  const policyHonest = self.includes('실제 셸 실행을 가로채진 않음'); // policy enforce: 차단 판정이 실행 가로채기 아님을 명시
3974
3974
  return tokenHonest && idHonest && policyHonest;
3975
3975
  } },
3976
+ { name: 'handoff 넛지 (1.36.7, 메타-테스트 도그푸드): 오래된 handoff→넛지 · 최근/부재→없음 · opt-out 존중 (행위)', run: () => {
3977
+ const os = require('os');
3978
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lz-hn-'));
3979
+ const hd = path.join(dir, '.harness'); fs.mkdirSync(hd);
3980
+ const lhf = path.join(hd, 'last-handoff.json');
3981
+ const iso = (msAgo) => new Date(Date.now() - msAgo).toISOString();
3982
+ try {
3983
+ const noneOk = _handoffNudgeState(dir) === null; // 부재 → 넛지 없음(신규 프로젝트 FP 방지)
3984
+ fs.writeFileSync(lhf, JSON.stringify({ last: iso(3 * 3600 * 1000), history: [iso(3 * 3600 * 1000)] }));
3985
+ const st = _handoffNudgeState(dir);
3986
+ const staleOk = !!st && st.gapMin >= 120; // 3h 오래됨 → 넛지
3987
+ fs.writeFileSync(lhf, JSON.stringify({ last: iso(5 * 60 * 1000), history: [iso(5 * 60 * 1000)] }));
3988
+ const freshOk = _handoffNudgeState(dir) === null; // 5min 최근 → 없음
3989
+ process.env.LEERNESS_NO_HANDOFF_NUDGE = '1';
3990
+ fs.writeFileSync(lhf, JSON.stringify({ last: iso(3 * 3600 * 1000), history: [] }));
3991
+ const optOutOk = _handoffNudgeState(dir) === null; // opt-out 존중
3992
+ delete process.env.LEERNESS_NO_HANDOFF_NUDGE;
3993
+ return noneOk && staleOk && freshOk && optOutOk;
3994
+ } finally { try { fs.rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ } }
3995
+ } },
3996
+ { name: '정직성 marginal 소진 (1.36.8): encoding passed 스코프 명시 + self-heal 범위 정직화 + 넛지 task update 확장 (소스 가드)', run: () => {
3997
+ const self = read(__filename);
3998
+ const encScopeOk = self.includes('encoding check passed (scope: allowlisted text extensions'); // passed 가 전체 트리 아님을 명시
3999
+ const selfHealOk = self.includes('secret 은 감지 전용') && self.includes('secret 감지 전용'); // identity(305) + capabilities(catalog) 양쪽
4000
+ const bareSelfHeal = '자동 감사 + ' + 'self-heal'; // split-literal: 옛 무제한 self-heal 문구 부재
4001
+ const noBareOk = !self.includes(bareSelfHeal);
4002
+ const nudgeInUpdate = /task updated: \$\{id\}`\);[\s\S]{0,200}_emitHandoffNudge\(root\)/.test(self); // update 경로 넛지 와이어
4003
+ return encScopeOk && selfHealOk && noBareOk && nudgeInUpdate;
4004
+ } },
3976
4005
  { name: 'CLI 영어화 Phase 1 (1.20.2, UR-0010): _uiLang 해석(flag>env>manifest>ko) + 첫화면 _t 적용 (행위+소스)', run: () => {
3977
4006
  const save = process.argv; const saveEnv = process.env.LEERNESS_LANG;
3978
4007
  try {
@@ -7000,6 +7029,29 @@ function _getLastHandoffGap(root) {
7000
7029
  return { hasLast: true, lastAt: j.last, gapMin, expected, isMiss, isLong, history: j.history || [] };
7001
7030
  } catch { return { hasLast: false }; }
7002
7031
  }
7032
+ // 1.36.7 (메타-테스트 도그푸드): 세션에서 작업 중(task/decision add)인데 handoff 가 오래됨/미실행이면 넛지.
7033
+ // 실측(트랜스크립트)에서 확인된 사각지대: compaction/resume 후 세션-시작 handoff 리추얼을 건너뜀 → 컨텍스트 미적재.
7034
+ // 그 신호는 handoff 출력에만 떴는데 정작 handoff 를 안 돌리는 게 문제라, 에이전트가 실제로 실행하는 명령에 넛지를 노출.
7035
+ // FP 가드: 기록 있음 + gap > 임계(기본 120분)일 때만(신규 프로젝트=기록 없음 → 넛지 안 함). opt-out: LEERNESS_NO_HANDOFF_NUDGE=1.
7036
+ const HANDOFF_NUDGE_MIN = 120;
7037
+ function _handoffNudgeState(root, thresholdMin = HANDOFF_NUDGE_MIN) {
7038
+ if (process.env.LEERNESS_NO_HANDOFF_NUDGE === '1') return null;
7039
+ try {
7040
+ const g = _getLastHandoffGap(root);
7041
+ if (g && g.hasLast && g.gapMin > thresholdMin) return { gapMin: g.gapMin };
7042
+ } catch {}
7043
+ return null;
7044
+ }
7045
+ function _emitHandoffNudge(root) {
7046
+ if (has('--json') || has('--quiet')) return; // 구조화/조용 모드 오염 방지
7047
+ const s = _handoffNudgeState(root);
7048
+ if (!s) return;
7049
+ const L = _uiLang(root); const tt = (ko, en) => (L === 'en' ? en : ko);
7050
+ const koAge = s.gapMin < 120 ? `${s.gapMin}분` : `${Math.floor(s.gapMin / 60)}시간`;
7051
+ const enAge = s.gapMin < 120 ? `${s.gapMin}min` : `${Math.floor(s.gapMin / 60)}h`;
7052
+ log(tt(` ⚠ 마지막 handoff ${koAge} 전 — 이 세션 컨텍스트 최신화를 위해 'leerness handoff .' 권장 (끄기: LEERNESS_NO_HANDOFF_NUDGE=1)`,
7053
+ ` ⚠ last handoff ${enAge} ago — run 'leerness handoff .' to refresh this session's context (off: LEERNESS_NO_HANDOFF_NUDGE=1)`));
7054
+ }
7003
7055
 
7004
7056
  // 1.9.198: B축 (멀티 Sub-Agent 오케스트라) 보강 — handoff 에서 task keyword 매칭 best agent 추천
7005
7057
  // 1.9.193 에서 agents multi --execute 결과를 lessons.md 에 기록 → 이를 keyword 매칭하여 추출
@@ -7701,6 +7753,7 @@ function taskAdd(root, text) {
7701
7753
  if (has('--json')) { log(JSON.stringify({ ok: true, id, status: _normTaskStatus(arg('--status', 'requested')), request: text })); return; }
7702
7754
  ok(`task added: ${id}`);
7703
7755
  _autoRoadmap(absRoot(root), 'data-change');
7756
+ _emitHandoffNudge(root); // 1.36.7: 세션 handoff 미실행/오래됨 넛지 (compaction/resume 사각지대)
7704
7757
  // 1.9.177: task add 자동 review-request trigger (사용자 명시 1.9.176 자동화).
7705
7758
  // 사용자가 task 추가 시 자동으로 사전 검토 — 충돌/재사용/효율/권장 단계 결과를 등록 직후 표시.
7706
7759
  // opt-out: --no-review 또는 env LEERNESS_NO_AUTO_REVIEW=1, 또는 status=in-progress/done 같은 운영 메타.
@@ -7781,6 +7834,7 @@ function taskUpdate(root, id) {
7781
7834
  if (has('--json')) { log(JSON.stringify({ ok: true, ...patch })); return; }
7782
7835
  ok(`task updated: ${id}`);
7783
7836
  _autoRoadmap(absRoot(root), 'data-change');
7837
+ _emitHandoffNudge(root); // 1.36.8: 넛지 확장 — 작업 중 가장 자주 실행하는 명령(update)에도 노출
7784
7838
  }
7785
7839
  function taskDrop(root, id) {
7786
7840
  if (!_requireInit(root, 'task drop')) return; // 1.9.396 (6번째 외부평가/codex P1-B): init 가드
@@ -8192,6 +8246,7 @@ function decisionAdd(root, title) {
8192
8246
  ok(`decision added: ${title}`);
8193
8247
  // 1.9.43+ handoff lessons 회수 흐름과 자동 통합 (decisions.md fuzzy 매칭됨)
8194
8248
  _autoRoadmap(absRoot(root), 'data-change');
8249
+ _emitHandoffNudge(root); // 1.36.7: 세션 handoff 미실행/오래됨 넛지 (compaction/resume 사각지대)
8195
8250
  }
8196
8251
 
8197
8252
  // 1.9.6: 옛 link 손실 row를 plan.md milestone과 자동 매칭 제안/복구.
@@ -8571,7 +8626,8 @@ function encodingCheck(root, opts = {}) {
8571
8626
  findings.forEach(f => log(` ${f.file} ${f.issue}`));
8572
8627
  process.exitCode = warnings > 0 ? 1 : 0;
8573
8628
  } else {
8574
- ok('encoding check passed');
8629
+ // 1.36.8 (정직성 marginal): 'passed' 는 전체 트리가 아니라 allowlist 텍스트 확장자(≤5MB)만 검사한 결과임을 명시.
8630
+ ok('encoding check passed (scope: allowlisted text extensions, files ≤5MB)');
8575
8631
  }
8576
8632
  }
8577
8633
 
@@ -18094,7 +18150,7 @@ function _leernessIdentity() {
18094
18150
  { key: 'policy', ko: '정책', desc: '8단계 권한 등급 + enforce (read-only→publish), MCP 호출 게이트' },
18095
18151
  { key: 'handoff', ko: '인수인계', desc: '에이전트 간 컨텍스트 표준 전달 (Claude→Codex→Goose), get_project_context 1콜 온보딩' },
18096
18152
  { key: 'verification', ko: '검증', desc: '근거 기반 완료 검증 (verify-claim --require-evidence) — 허위 완료 감지 (권고; CI 게이트 시 차단)' },
18097
- { key: 'audit', ko: '감사', desc: 'drift/idempotency/secret/encoding 자동 감사 + self-heal (--auto-fix)' }
18153
+ { key: 'audit', ko: '감사', desc: 'drift/idempotency/secret/encoding 자동 감사 (self-heal: drift·idempotency --auto-fix, encoding --apply; secret 감지 전용)' }
18098
18154
  ],
18099
18155
  complements: 'AGENTS.md(정적 지침)을 대체하지 않고 보완 — 정적 규칙/명령/금지는 AGENTS.md, 동적 상태·기억·검증·인수인계는 leerness',
18100
18156
  entryPoints: { context: 'leerness context (MCP get_project_context)', mcp: 'leerness mcp serve', adapter: 'leerness adapter <tool>' },
@@ -21178,5 +21234,6 @@ module.exports = {
21178
21234
  _vcImplIsEmpty, _VC_EMPTY_SHELL_RE,
21179
21235
  // 1.18.3 (UR-0003): 분야별 자기질문 품질 렌즈 — 단위 테스트. 1.19.2: 파일→도메인 매핑(완료-검증 advisory)
21180
21236
  LENS_CATALOG, lensCmd, _lensDomainsForFiles, _mergeLensCatalog, _loadProjectLenses, _effectiveLensCatalog,
21181
- _isDbContentText, _anyDbContentInFiles, _withDbDomain
21237
+ _isDbContentText, _anyDbContentInFiles, _withDbDomain,
21238
+ _handoffNudgeState, _getLastHandoffGap, _recordLastHandoff
21182
21239
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.6",
3
+ "version": "1.36.8",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",