leerness 1.9.439 → 1.9.440

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.440 — 2026-06-08 — 🛡️ [안정화/Stable] 시크릿 스캐너 prefix 패턴 placeholder 가드 (12th 외부평가 Opus P2, UR-0140)
4
+
5
+ **🛡️ 안정화 릴리스(Stable) — 보안 스캐너 False-Positive 가 CI 를 깨던 문제 수정.**
6
+
7
+ ### 변경 (12th 외부 멀티모델 리뷰 Opus P2)
8
+ - **`scan secrets`/`gate`/`audit` 시크릿 스캐너**: placeholder 가드(`_isPlaceholderSecret`)가 `valueGroup` 있는 2개 패턴에만 적용돼, **AWS/GitHub 등 19개 prefix 패턴은 가드를 우회**(1.9.436 픽스가 그들에겐 dead code). → `.env.example`(placeholder 전용, gitignore 대상 아님)의 더미 토큰(`AKIAXXXX…`/`ghp_XXXX…`)이 커밋 시크릿으로 오탐 → `scan/gate/audit` 실패(exit 1) → CI 파손.
9
+ - **수정**: `valueGroup` 없으면 전체 매치(`m[0]`)로 placeholder 판정 — 19 prefix 패턴도 8연속 더미/마커 가드 적용. `requireSecretLike` 는 valueGroup 한정 유지. **진짜 키(고엔트로피)는 그대로 탐지**(회귀 0), `sk-EXAMPLE…` 류 실키-FN 정책도 유지.
10
+ - 통합 갭 방지: selftest 가 순수 함수만이 아닌 **스캐너 경로**도 검증(Opus 지적), E2E B(1.9.440) 추가(.env.example 더미 미탐 + 진짜 탐지).
11
+
12
+ ### 검증 (회귀 0)
13
+ - **selftest 184→185** + **E2E 신규 B(1.9.440)**: 더미 prefix 토큰 미탐(exit 0) + 진짜 AWS 키 탐지(exit 1) + sk-EXAMPLE 실키 유지.
14
+ - 맹신 X: 내 1.9.436 의 불완전성(prefix 패턴 미적용)을 12th 리뷰가 적발, 직접 재현·수정. P3(AWS …EXAMPLE 키 FP)는 기존 FN 정책 충돌로 UR-0144 분리.
15
+
16
+ ### 안정화 표시 (R-0006)
17
+ CHANGELOG [안정화/Stable] · git tag/GitHub release (Stable) · npm dist-tag `stable`.
18
+
3
19
  ## 1.9.439 — 2026-06-08 — drift --auto-fix --json 순수성 (10th 외부평가 Codex P1, UR-0135 완결)
4
20
 
5
21
  **📐 `drift check --auto-fix --json` 이 dirty 워크스페이스에서 진행로그를 stdout 에 섞던 비-순수 JSON 해소.**
package/README.md CHANGED
@@ -1,477 +1,165 @@
1
- # Leerness
1
+ # leerness
2
2
 
3
- > **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
4
- > **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
3
+ > **AI 코딩 에이전트를 위한 운영 레이어(operating layer).** 코드를 대신 쓰는 도구가 아니라, AI 에이전트의 **기억·인수인계·검증·감사·보안 가드**를 프로젝트에 영속화하는 CLI + MCP 서버입니다.
5
4
 
6
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.439-green)]() [![tests](https://img.shields.io/badge/e2e-360%2F360-success)]() [![selftest](https://img.shields.io/badge/selftest-184-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-85-brightgreen)]() [![providers](https://img.shields.io/badge/AI_providers-10-brightgreen)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
5
+ [![npm](https://img.shields.io/npm/v/leerness)](https://www.npmjs.com/package/leerness) · ![MCP tools](https://img.shields.io/badge/MCP--tools-85-blue) · **런타임 의존성 0** · **install-script 0** · offline-first · Node ≥ 18 · MIT
7
6
 
8
- ```
9
- ╔══════════════════════════════════════════════════════════════╗
10
- ║ ██╗ ███████╗███████╗██████╗ ███╗ ██╗███████╗███████╗ ║
11
- ║ ██║ ██╔════╝██╔════╝██╔══██╗████╗ ██║██╔════╝██╔════╝ ║
12
- ║ ██║ █████╗ █████╗ ██████╔╝██╔██╗ ██║█████╗ ███████╗ ║
13
- ║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
14
- ║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
15
- ║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
16
- ║ AI Agent Reliability Harness (버전: 상단 배지 참조) ║
17
- ║ verify · remember · orchestrate · audit · drift · roles ║
18
- ╚══════════════════════════════════════════════════════════════╝
19
- ```
20
-
21
- <p align="center"><b>🇰🇷 <a href="#한국어">한국어</a> &nbsp;·&nbsp; 🇬🇧 <a href="#english">English</a></b></p>
22
-
23
- ---
24
-
25
- <a id="한국어"></a>
26
-
27
- # 🇰🇷 한국어
28
-
29
- ## 한눈에 보기
30
-
31
- `leerness`는 **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok · opencode · Qwen · Aider · Goose** 같은 AI 코딩 에이전트와 함께 일할 때 생기는 구조적 문제를 자동으로 막아주는 CLI 도구입니다.
32
-
33
- AI는 코드를 빠르게 쓰지만, 다음 **5가지 함정**에 반복해서 빠집니다 — leerness는 이를 모두 자동으로 감지·차단·회복합니다.
34
-
35
- | # | 함정 | leerness의 방어 |
36
- |---|------|-----------------|
37
- | 1 | **거짓 완료** — "구현했습니다"인데 코드 변경/테스트가 없음 | `verify-claim --run-tests` (증거 파일 + 실제 테스트 실행) |
38
- | 2 | **중복 생성** — 이미 있는 함수를 또 만듦 | `reuse-map` (워크스페이스 전체 중복 감지) |
39
- | 3 | **망각** — 다음 세션에서 계획·결정을 잃음 | `handoff` (컨텍스트 자동 적재) |
40
- | 4 | **충돌** — 멀티 에이전트가 같은 파일을 동시에 수정 | `agents dispatch` (경로 격리 + 역할 분배) |
41
- | 5 | **도구 drift** — 시간이 지나며 도구·메타파일을 잊음 | `drift check` (자동 감지 + 회복) |
7
+ > 이 문서는 **외부 다중 모델(Codex / Claude Sonnet / Claude Opus)이 README 를 보지 않고 leerness 를 직접 설치·실행·소스 분석한 객관 리뷰**를 바탕으로 재구성되었습니다. 매 릴리스마다 자동 갱신됩니다.
42
8
 
43
9
  ---
44
10
 
45
- ## ⏱️ 60초 시작
46
-
47
- ```bash
48
- # 1) 설치 + 초기화 — 시스템 언어를 자동 감지해 한/영 가이드 표시 (1.9.269)
49
- npx leerness@latest init .
50
-
51
- # 2) AI 세션 시작 — 컨텍스트 자동 적재
52
- npx leerness handoff .
11
+ ## leerness가 뭔가요?
53
12
 
54
- # 3) AI 작업 "정말 했는지" 증거로 검증
55
- npx leerness verify-claim T-0001 --run-tests
13
+ AI 코딩 에이전트(Claude Code, Cursor, Codex, Aider, Goose 등) 코드를 쓰지만 가지 약점이 있습니다.
56
14
 
57
- # 4) 세션 마감 다음 세션으로 자동 인계
58
- npx leerness session close .
59
- ```
15
+ 1. **기억하지 못합니다**세션이 바뀌면 현재 상태·결정·다음 작업을 잊습니다.
16
+ 2. **거짓 완료를 선언합니다** — 증거(파일·테스트·로그) 없이 "완료했습니다"라고 말합니다.
17
+ 3. **표준이 없습니다** — 여러 에이전트 간 인수인계, 보안/인코딩 점검, 드리프트 관리가 제각각입니다.
60
18
 
61
- > 💡 `@latest`를 붙이면 npx 캐시로 버전이 실행되는 함정을 피할 있습니다.
19
+ leerness는 문제들을 해결하는 **외부 운영 substrate**입니다. 어떤 에이전트 위에도 얹어, 프로젝트의 상태를 `.harness/` 파일로 영속화하고 CLI · MCP 도구로 노출합니다. **leerness 자체는 LLM을 호출하거나 코드를 실행하지 않습니다.**
62
20
 
63
21
  ---
64
22
 
65
- ## 🧩 작동 방식
66
-
67
- ### 1. 워크스페이스에 "기억 채널"을 만듭니다
68
-
69
- ```
70
- .harness/
71
- ├── plan.md ← 무엇을 할 것인가
72
- ├── progress-tracker.md ← 무엇을 했는가 (증거 포함)
73
- ├── decisions.md ← 왜 그렇게 했는가
74
- └── session-handoff.md ← 다음 세션에 무엇을 넘기는가
75
- ```
76
-
77
- 이 파일들은 **사용자 메모리로 보호**됩니다. leerness가 자동 갱신하되, 사람이나 AI가 직접 쓴 내용은 **항상 보존**되고 변경 전 `.harness/archive/`에 백업됩니다.
78
-
79
- ### 2. AI가 "완료"라고 할 때마다 증거로 검증
80
-
81
- ```
82
- AI: "T-0042 API 호출 구현 완료했습니다"
83
- ↓ leerness verify-claim T-0042 --run-tests
84
- evidence 파일 확인 → 테스트 실제 실행 → 코드 매칭
85
-
86
- ✓ 검증 통과 또는 ✗ "코드에 fetch 호출 흔적 없음 (신뢰도 0.85)"
87
- ```
88
-
89
- ### 3. 여러 AI 에이전트를 역할별로 지휘 (1.9.270 신규)
90
-
91
- 모델마다 강점이 다릅니다. **역할을 모델에 매핑**해 적재적소로 일을 시킵니다.
92
-
93
- ```bash
94
- leerness roles set 코딩 --provider codex --model gpt-5.5 # 코딩 담당
95
- leerness roles set 검수자 --provider claude --model claude-opus-4-7 # 검수 담당
96
- leerness roles suggest # 활성 에이전트 기반 최적 배치 + 근거 추천
97
- leerness agents dispatch "이 변경 검수" --role 검수자 # 역할 → 모델 자동 라우팅
98
- ```
99
-
100
- > 역할 7종: 지휘(commander)·검수(reviewer)·코딩(coder)·설계(architect)·디자인(designer)·디버그(debugger)·분배(dispatcher). 한국어 별칭 지원.
101
-
102
- ### 4. 라운드가 길어지면 "leerness를 안 쓰는" drift를 자동 감지
103
-
104
- ```bash
105
- leerness drift check .
106
- → 🔴 critical (110/200) — session close 11일 누락
107
- → 권장: leerness session close . (1회 실행으로 🟠 attention 회복)
108
- ```
109
-
110
- ### AGENTS.md(정적) vs leerness(동적) — 보완 관계
111
-
112
- leerness는 [AGENTS.md](https://agents.md)를 **대체하지 않고 보완**합니다.
23
+ ## 해결하는 문제
113
24
 
114
- | 정적 — AGENTS.md | 동적 leerness |
25
+ | 문제 | leerness의 해법 |
115
26
  |---|---|
116
- | 코딩 규칙·테스트 명령·금지·배포 절차 (자주 안 변함) | 현재 목표·수정 파일·실패 시도·검증 결과·다음 인수인계 (매 작업 변함) |
117
- | 사람이 작성 | `leerness state`/MCP `leerness_state_*` `.leerness/` 기록 |
118
-
119
- 규칙은 AGENTS.md, **작업 상태/기억/검증/인수인계는 leerness** (모든 에이전트 공통 운영 레이어).
120
-
121
- ---
122
-
123
- ## 📊 적용 효과 (정량)
124
-
125
- `leerness-bench` 28 프로젝트 124 task 측정:
126
-
127
- | 항목 | 적용 | 미적용 |
128
- |------|-----:|-------:|
129
- | 다중 에이전트 효율 | 100/100 | 3/100 |
130
- | 자동 검수 (verify-claim) | 98/100 | 0/100 |
131
- | 재사용 인식 | 100/100 | 0/100 |
132
- | 자동 BUG 감지 | 100/100 | 0/100 |
133
- | 컨텍스트 유지 | 100/100 | 0/100 |
134
- | **종합** | **597/600 (99%)** | **3/600 (0.5%)** |
135
-
136
- - 수동 검수 **90초 → 자동 1.5초**
137
- - 컨텍스트 적재 **500자** (`handoff --compact`, AI 토큰 90% 절감)
138
-
139
- ---
140
-
141
- ## 🛠️ 핵심 명령
142
-
143
- ```bash
144
- # 일상
145
- leerness init [path] # 신규 설치 (시스템 언어 자동 감지)
146
- leerness handoff [path] [--compact] # 세션 시작 컨텍스트 적재
147
- leerness verify-claim T-XXX --run-tests # 거짓 완료 검증
148
- leerness audit [path] [--fix] # 일관성 감사 (--fix 자동 갱신)
149
- leerness session close [path] # 세션 마감 + 다음 세션 인계
150
- leerness drift check [path] # leerness 미사용 drift 점수
151
-
152
- # 멀티 AI 에이전트 · 역할
153
- leerness setup-agents # 활성화 (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
154
- leerness agents list | quota # 상태 / 한도
155
- leerness agents dispatch "<task>" --role coder # 역할 기반 모델 라우팅
156
- leerness agents bench "<task>" # 여러 CLI 동시 호출 + 비교
157
- leerness roles list | set | suggest | verify # 모델별 역할 부여
158
-
159
- # 보안 · 인코딩
160
- leerness scan secrets . # API 키/토큰 패턴 스캔
161
- leerness encoding check . # BOM/UTF-16/한글 라운드트립
162
- leerness gate [path] # verify+audit+scan+encoding+lazy 통합
163
-
164
- # 버전
165
- leerness update --check # 새 버전 감지
166
- leerness update --yes # 자동 마이그레이션 + 검증
167
- ```
168
-
169
- 전체 명령은 `leerness commands`, MCP 도구는 `leerness mcp serve`로 확인하세요.
170
-
171
- ---
172
-
173
- ## ❓ FAQ
174
-
175
- **Q. leerness가 내 코드를 바꾸나요?**
176
- A. 사용자 메모리(plan/progress/decisions)는 **항상 보존**, 모든 변경 전 자동 백업합니다. 소스 코드는 직접 수정하지 않습니다.
177
-
178
- **Q. AI 에이전트 없이도 쓸 수 있나요?**
179
- A. 네. 1인 개발자의 작업 검증·기억·중복 감지에도 유용합니다.
180
-
181
- **Q. 외부 AI CLI를 자동으로 호출하나요?**
182
- A. **절대 아니요.** `agents dispatch`는 실행 명령 텍스트만 생성합니다. 실제 실행은 사용자/메인 에이전트가 명시적으로 합니다.
183
-
184
- **Q. CI에서 쓸 수 있나요?**
185
- A. 네. 모든 명령이 exit code + `--json` + `--yes` 비대화형을 지원합니다.
27
+ | 세션 맥락·결정·다음 작업 유실 | `task`/`decision`/`lesson`/`plan`/`rule` 메모리를 영속화하고, 세션 시작 `handoff` 1콜로 회수 |
28
+ | 증거 없는 "완료" 주장 | `verify-claim --require-evidence`, `lazy detect`, anti-lazy 정책으로 차단 |
29
+ | Claude → Codex → Cursor 교체 시 맥락 소실 | 표준 세션 워크플로(handoff → 작업 → session close)로 에이전트 독립 인수인계 |
30
+ | 하드코딩 시크릿 · 인코딩 깨짐(BOM/CP949) | `scan secrets`, `encoding check` 자동 감지 (CI 게이트) |
31
+ | 워크스페이스 노화(drift)를 에이전트가 모름 | `drift check [--auto-fix]` 점수화 + 자동 회복 |
32
+ | 명세 ↔ 구현 불일치 | `contract verify spec.md impl.js` 함수/필드 일치 검사 |
33
+ | 여러 AI CLI의 역할/분배 표준 부재 | `agents`/`roles`/`team` 오케스트레이션(기본 opt-in, dispatch는 실행이 아닌 명령 생성) |
186
34
 
187
35
  ---
188
36
 
189
- ## 🔒 보안·투명성
190
-
191
- leerness 는 권한이 큰 CLI 하네스입니다(child_process · git · 외부 CLI · 자동화 브리지 · hook 설치). **할 수 있는 전부와 끄는 법**을 공개합니다.
37
+ ## 빠른 시작
192
38
 
193
39
  ```bash
194
- leerness capabilities # 권한 표면 + opt-out + 주의 명령 (--json 지원)
195
- ```
196
-
197
- - **런타임 의존성 0** · **postinstall 없음** · **변경 전 자동 백업** · **동의 없는 외부 호출 금지**
198
- - `init` 시 `.claude/settings.local.json` 에 SessionStart hook(`update --check`)이 설치됩니다 → 끄기: `leerness init . --no-auto-update`
199
- - 회사/운영 코드에서는 먼저 한 프로젝트에서 `init` 후 `git diff` 로 검토하고 커밋하세요.
200
- - 자세히: [SECURITY.md](SECURITY.md)
40
+ # 설치 (런타임 의존성 0 추가 패키지 없음)
41
+ npm install -g leerness
201
42
 
202
- **설치 부담 완화 옵션 (1.9.276):**
43
+ # 프로젝트 초기화 (.harness/ 거버넌스 문서 + .claude/.cursor/.github 어댑터 생성)
44
+ leerness init .
203
45
 
204
- ```bash
205
- leerness init . --dry-run # 생성/수정될 파일 미리보기 (실제 변경 0)
206
- leerness init . --minimal # 핵심 파일만 (에디터 통합/가이드/roadmap/.env 생략)
207
- leerness init . --no-env # .env/.env.example 자동 생성만 생략
208
- ```
46
+ # 세션 시작 — 이전 맥락/기억/다음 작업을 1콜로 회수
47
+ leerness handoff .
209
48
 
210
- **도구별 어댑터 (1.9.280) — 내 에이전트만 연결:**
49
+ # ... 에이전트가 작업 ...
50
+ leerness task add "사용자 인증 API 구현"
51
+ leerness gate . # verify + audit + scan secrets + encoding + lazy 통합 게이트
211
52
 
212
- ```bash
213
- leerness init . --minimal --no-env # 1) 최소 설치 / minimal install
214
- leerness adapter cursor # 2) 내 도구만 (.cursor + .mcp.json) / only my tool
215
- leerness adapter list # 가능 어댑터 (claude/cursor/codex/goose/opencode/aider/qwen/...)
216
- # MCP 지원 도구는 .mcp.json 에 leerness 등록 → 상태 verb(state_*) 직접 호출
53
+ # 세션 종료 — 마감 통계 + 다음 라운드 추천 + 인수인계 문서 자동 생성
54
+ leerness session close .
217
55
  ```
218
56
 
219
- ### 릴리스 채널 (안정 vs 실험)
220
-
221
- leerness는 활발히 개발됩니다(잦은 1.9.x). 채널을 골라 안정성을 제어하세요:
222
-
223
- ```bash
224
- npm i leerness # latest (안정) — 일반 사용자 기본
225
- npm i leerness@next # next (실험) — 조기 검증용
226
- npm i leerness@1.9.275 # 버전 고정 — 재현성 (운영 코드 권장)
227
- leerness release channel # 현재 채널/정책 확인 (--json)
228
- ```
57
+ CLAUDE.md / AGENTS.md `세션 시작 시 leerness handoff .`, `종료 전 leerness session close .` 지침을 추가하면 에이전트가 자동으로 호출합니다.
229
58
 
230
59
  ---
231
60
 
232
- <a id="english"></a>
233
-
234
- # 🇬🇧 English
235
-
236
- ## At a glance
237
-
238
- `leerness` is a CLI harness that automatically prevents the structural problems that arise when working with AI coding agents like **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok · opencode · Qwen · Aider · Goose**.
239
-
240
- AI writes code fast, but repeatedly falls into **5 traps** — leerness detects, blocks, and recovers from all of them.
61
+ ## 핵심 개념 — 5계층
241
62
 
242
- | # | Trap | leerness defense |
243
- |---|------|------------------|
244
- | 1 | **Fake completion** "done!" but no code change / no tests run | `verify-claim --run-tests` (evidence files + real test execution) |
245
- | 2 | **Duplication**rebuilding a function that already exists | `reuse-map` (workspace-wide duplicate detection) |
246
- | 3 | **Amnesia**losing plans/decisions across sessions | `handoff` (automatic context loading) |
247
- | 4 | **Collisions** — multiple agents editing the same file | `agents dispatch` (path isolation + role routing) |
248
- | 5 | **Tool drift** — forgetting tools/meta files over time | `drift check` (auto detection + recovery) |
63
+ - **기억(Memory)** `task`/`plan`/`decision`/`lesson`/`rule`/`feature`(그래프). canonical JSON 단일 진실소스로 저장하고 마크다운은 projection. archive/restore 지원.
64
+ - **인수인계(Handoff)** — `handoff`(세션 시작 컨텍스트), `session close`(마감 보고). 에이전트 교체에도 맥락 보존.
65
+ - **검증(Verification)** — `verify-claim`(증거 강제), `contract verify`(명세↔구현), `verify-code`(테스트/빌드 실행 + 증거 기록).
66
+ - **감사(Audit)** — `scan secrets`, `encoding check`, `lazy detect`, `drift check`, `audit`, 그리고 이를 묶는 `gate`.
67
+ - **정책/보안(Policy)** — 8단계 권한 등급 enforce, 외부 에이전트 opt-in, 자연어 영구 룰(`rule add ... --trigger`).
249
68
 
250
69
  ---
251
70
 
252
- ## ⏱️ 60-second start
71
+ ## 명령 카테고리
253
72
 
254
- ```bash
255
- # 1) Install + init auto-detects system language for KO/EN guides (1.9.269)
256
- npx leerness@latest init .
257
-
258
- # 2) Start an AI session auto-load context
259
- npx leerness handoff .
260
-
261
- # 3) After AI claims "done" verify with evidence
262
- npx leerness verify-claim T-0001 --run-tests
263
-
264
- # 4) Close the session — hand off to the next one
265
- npx leerness session close .
266
- ```
73
+ - **초기화/진단**: `init` · `status` · `which` · `doctor` · `selftest` · `capabilities`
74
+ - **메모리**: `task` · `plan` · `decision` · `lesson` · `rule` · `feature` · `memory status/search`
75
+ - **인수인계/세션**: `handoff` · `session close` · `pulse` · `health`
76
+ - **검증/감사**: `check` · `gate` · `audit` · `drift check` · `lazy detect` · `scan secrets` · `encoding check` · `verify-code` · `verify-claim` · `contract verify`
77
+ - **외부 에이전트**: `agents list/check/dispatch` · `provider` · `roles` · `adapter`
78
+ - **운영/확장**: `release` · `migrate` · `team` · `install-safety` · `route` · `review`(페르소나)
79
+ - **브리지(opt-in)**: `web`(playwright) · `pc`(robotjs) · `lsp`
80
+ - **MCP**: `mcp serve` stdio JSON-RPC 서버로 85개 도구 노출
267
81
 
268
- > 💡 Adding `@latest` avoids the trap of npx running a stale cached version.
82
+ 전체 명령은 `leerness commands` 또는 `leerness --help` 확인하세요.
269
83
 
270
84
  ---
271
85
 
272
- ## 🧩 How it works
273
-
274
- ### 1. Creates "memory channels" in your workspace
275
-
276
- ```
277
- .harness/
278
- ├── plan.md ← what to do
279
- ├── progress-tracker.md ← what was done (with evidence)
280
- ├── decisions.md ← why it was done that way
281
- └── session-handoff.md ← what to pass to the next session
282
- ```
283
-
284
- These are **protected as user memory**. leerness updates them automatically, but anything a human or AI wrote is **always preserved** and backed up to `.harness/archive/` before any change.
285
-
286
- ### 2. Verifies every "done" with evidence
287
-
288
- ```
289
- AI: "Finished implementing the T-0042 API call"
290
- ↓ leerness verify-claim T-0042 --run-tests
291
- check evidence files → actually run tests → match code
292
-
293
- ✓ verified or ✗ "no trace of a fetch call in code (confidence 0.85)"
294
- ```
295
-
296
- ### 3. Directs multiple AI agents by role (new in 1.9.270)
297
-
298
- Each model has different strengths. **Map roles to models** to put the right model on the right job.
86
+ ## 대표 워크플로
299
87
 
88
+ **기본 세션 사이클**
300
89
  ```bash
301
- leerness roles set coder --provider codex --model gpt-5.5
302
- leerness roles set reviewer --provider claude --model claude-opus-4-7
303
- leerness roles suggest # recommend optimal layout from active agents + rationale
304
- leerness agents dispatch "review this change" --role reviewer # role → model auto-routing
90
+ leerness handoff . # 이전 맥락 자동 로드
91
+ leerness task add "API 응답 검증 로직 구현"
92
+ leerness task update T-0001 --status in-progress --evidence "검증 함수 구현"
93
+ leerness gate . # 배포 통합 품질 게이트
94
+ leerness session close . # 마감 + 다음 에이전트 인수인계
305
95
  ```
306
96
 
307
- > 7 roles: commander · reviewer · coder · architect · designer · debugger · dispatcher.
308
-
309
- ### 4. Detects "drift" when you stop using leerness over long rounds
310
-
97
+ **보안·검증 게이트 (CI)**
311
98
  ```bash
312
- leerness drift check .
313
- 🔴 critical (110/200) session close missing for 11 days
314
- → recommended: leerness session close . (one run recovers to 🟠 attention)
99
+ leerness scan secrets . # 커밋 대상 하드코딩 시크릿 → exit 1
100
+ leerness contract verify spec.md src/api.js # 명세 함수/필드 누락 exit 1
101
+ leerness verify-claim T-0001 --require-evidence
315
102
  ```
316
103
 
317
- ### AGENTS.md (static) vs leerness (dynamic) — complementary
318
-
319
- leerness **complements, not replaces** [AGENTS.md](https://agents.md).
320
-
321
- | Static — AGENTS.md | Dynamic — leerness |
322
- |---|---|
323
- | coding rules, test commands, prohibitions, deploy steps (rarely change) | current goal, files changed, failed attempts, verification, next handoff (change every task) |
324
- | written by humans | recorded to `.leerness/` via `leerness state` / MCP `leerness_state_*` |
325
-
326
- → Rules live in AGENTS.md; **work state/memory/verification/handoff live in leerness** (the shared operating layer for any agent).
327
-
328
- ---
329
-
330
- ## 📊 Measured impact
331
-
332
- From `leerness-bench` across 28 projects / 124 tasks:
333
-
334
- | Metric | With | Without |
335
- |--------|-----:|--------:|
336
- | Multi-agent efficiency | 100/100 | 3/100 |
337
- | Auto-verification (verify-claim) | 98/100 | 0/100 |
338
- | Reuse awareness | 100/100 | 0/100 |
339
- | Auto bug detection | 100/100 | 0/100 |
340
- | Context retention | 100/100 | 0/100 |
341
- | **Total** | **597/600 (99%)** | **3/600 (0.5%)** |
342
-
343
- - Manual review **90s → 1.5s automated**
344
- - Context load **~500 chars** (`handoff --compact`, ~90% fewer AI tokens)
345
-
346
- ---
347
-
348
- ## 🛠️ Core commands
349
-
104
+ **다중 에이전트 조율**
350
105
  ```bash
351
- # Daily
352
- leerness init [path] # new install (auto-detects system language)
353
- leerness handoff [path] [--compact] # load session-start context
354
- leerness verify-claim T-XXX --run-tests # verify fake completion
355
- leerness audit [path] [--fix] # consistency audit (--fix auto-update)
356
- leerness session close [path] # close session + hand off
357
- leerness drift check [path] # leerness-disuse drift score
358
-
359
- # Multi AI agents · roles
360
- leerness setup-agents # enable (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
361
- leerness agents list | quota # status / limits
362
- leerness agents dispatch "<task>" --role coder # role-based model routing
363
- leerness agents bench "<task>" # call several CLIs at once + compare
364
- leerness roles list | set | suggest | verify # per-model role assignment
365
-
366
- # Security · encoding
367
- leerness scan secrets . # API key/token pattern scan
368
- leerness encoding check . # BOM/UTF-16/CJK round-trip
369
- leerness gate [path] # verify+audit+scan+encoding+lazy combined
370
-
371
- # Versions
372
- leerness update --check # detect new version
373
- leerness update --yes # auto-migrate + verify
106
+ leerness agents list # 설치된 외부 AI CLI 가용성
107
+ leerness agents dispatch "코드 리뷰" --to codex # 실행 명령 생성(직접 실행은 사용자/메인 에이전트)
374
108
  ```
375
109
 
376
- Run `leerness commands` for the full list, or `leerness mcp serve` to expose tools to a main agent.
377
-
378
- ---
379
-
380
- ## ❓ FAQ
381
-
382
- **Q. Does leerness change my code?**
383
- A. User memory (plan/progress/decisions) is **always preserved** and backed up before any change. It never edits your source code directly.
384
-
385
- **Q. Can I use it without an AI agent?**
386
- A. Yes. Solo developers can use it for self-verification, memory, and duplicate detection.
387
-
388
- **Q. Does it auto-call external AI CLIs?**
389
- A. **Never.** `agents dispatch` only generates command text. Actual execution is explicit, by you or your main agent.
390
-
391
- **Q. Can I use it in CI?**
392
- A. Yes. Every command supports exit codes, `--json`, and non-interactive `--yes`.
393
-
394
- ---
395
-
396
- ## 🔒 Security & transparency
397
-
398
- leerness is a powerful CLI harness (child_process, git, external CLIs, automation bridges, hook install). It discloses **everything it can do and how to turn each off**.
399
-
110
+ **MCP (외부 AI 에이전트에 도구로 노출)**
400
111
  ```bash
401
- leerness capabilities # capability surface + opt-out + cautious commands (--json supported)
112
+ leerness mcp serve # JSON-RPC over stdio, 85 도구
402
113
  ```
403
114
 
404
- - **Zero runtime deps** · **no postinstall** · **auto-backup before changes** · **never auto-calls external services without consent**
405
- - `init` installs a SessionStart hook (`update --check`) in `.claude/settings.local.json` → disable with `leerness init . --no-auto-update`
406
- - For company/production code, trial `init` in one project, review with `git diff`, then commit.
407
- - Details: [SECURITY.md](SECURITY.md)
408
-
409
- **Lighter-install options (1.9.276):**
410
-
411
- ```bash
412
- leerness init . --dry-run # preview files to be created/modified (zero changes)
413
- leerness init . --minimal # core files only (skips editor integrations/guides/roadmap/.env)
414
- leerness init . --no-env # skip only .env/.env.example auto-creation
415
- ```
416
-
417
- ### Release channels (stable vs experimental)
115
+ ---
418
116
 
419
- leerness is actively developed (frequent 1.9.x). Pick a channel to control stability:
117
+ ## 아키텍처 (외부 리뷰 검증)
420
118
 
421
- ```bash
422
- npm i leerness # latest (stable) default for most users
423
- npm i leerness@next # next (experimental) early validation
424
- npm i leerness@1.9.275 # pin a version reproducibility (recommended for prod)
425
- leerness release channel # show current channel/policy (--json)
426
- ```
119
+ - **런타임 의존성 0 / install-script 0** — `package.json` 의 dependencies/optional/peer 가 전부 비어 있고 postinstall 도 없습니다. 순수 Node stdlib(`fs`/`path`/`child_process`/`readline`). 공급망 공격면 최소. `leerness install-safety` 로 확인 가능.
120
+ - **canonical JSON 단일 진실소스 + 마크다운 projection** — 메모리는 JSON 으로 저장하고 사람이 읽는 `.md` 는 파생물. 파이프(`|`)·개행·백틱·이모지·한글이 마크다운 테이블에서도 안전(셀 이스케이프 + round-trip).
121
+ - **원자적 UTF-8 쓰기**temp + rename 으로 부분쓰기 손상 방지, BOM 자동 strip.
122
+ - **shell 미경유 MCP** `mcp serve` 도구 호출은 셸을 거치지 않고 인자를 직접 전달(명령 주입 차단).
123
+ - **순수 `--json` / 일관 exit code** — 성공·실패·미존재 경로 모두 파싱 가능한 JSON(`{ok,error,code}`) + 실패 시 exit 1. 자동화·CI 친화.
124
+ - **모듈 분리(DI)** + **내장 자가검증** — `lib/` 순수 유틸/IO/카탈로그 분리, `selftest` 180+ 케이스로 설치 무결성 검증.
427
125
 
428
126
  ---
429
127
 
430
- ## 🔧 Environment variables / 환경변수
128
+ ## 차별점
431
129
 
432
- | Variable | Effect |
433
- |----------|--------|
434
- | `LEERNESS_OFFLINE=1` | Skip npm calls (offline) / npm 호출 스킵 |
435
- | `LEERNESS_ENABLE_CLAUDE/CODEX/AGY/GROK/COPILOT/OLLAMA` | Enable external CLIs / 외부 CLI 활성화 |
436
- | `LEERNESS_NO_BANNER` | Skip ASCII banner / 배너 스킵 |
437
- | `LEERNESS_NO_DRIFT_CHECK` | Disable drift warning / drift 경고 끄기 |
438
- | `LEERNESS_NO_INTERACTIVE` | Disable arrow-key select / 방향키 선택 끄기 |
130
+ - **진짜 0-dependency** SaaS 가 아니라 파일 기반 로컬 운영 메모리. 네트워크 불필요(업데이트 확인 제외).
131
+ - **에이전트 중립** — Claude / Codex / Cursor / Aider / Goose 등 어디에나 적용.
132
+ - **거짓 완료 방지** 완료 주장에 evidence 요구하는 anti-lazy 흐름.
133
+ - **통합 게이트** 보안·인코딩·드리프트·검증을 `gate` 하나로.
134
+ - **한국어 우선 + Windows/인코딩 1급 시민** — CP949/BOM 가드, 한국어 출력 기본(`--language en` 지원).
135
+ - **자기기술(self-describing)** — `about`/`commands`/`capabilities`/`doctor` 도구가 스스로 설명.
136
+ - **지속적 외부 검증** 다중 모델 클린룸 리뷰로 회귀 0 을 유지하며 진화.
439
137
 
440
138
  ---
441
139
 
442
- ## 📜 Recent highlights / 최근 변경
140
+ ## 보안
443
141
 
444
- - **1.9.271** README 한/영 이중 사용자 친화 재작성 / Bilingual KO-EN README rewrite.
445
- - **1.9.270** agent roles: 모델별 역할 부여 / per-model role assignment (`roles` CLI + `dispatch --role`).
446
- - **1.9.269** init OS 시스템 언어 감지 / OS system-language detection on init.
447
- - **1.9.268** — grok 정식 provider 승격 / grok promoted to a first-class provider.
448
- - **1.9.265~267** — CLI 에이전트 슬래시 명령 레지스트리 + `--help` probe 자동 갱신.
449
-
450
- > 전체 이력 / full history: [CHANGELOG.md](CHANGELOG.md)
142
+ - 시크릿/키/토큰을 소스에 하드코딩하지 마세요. `.env` 사용 + `.gitignore` 포함. `scan secrets`/`gate` 커밋 대상 시크릿을 차단합니다.
143
+ - 외부 AI CLI 연동은 **기본 비활성**입니다(`LEERNESS_ENABLE_*` opt-in). 브리지(web/pc/lsp)도 opt-in.
144
+ - 자세한 권한 표면은 [SECURITY.md](./SECURITY.md) 참고.
451
145
 
452
146
  ---
453
147
 
454
- ## 🤝 Contributing / 기여
455
-
456
- - Issues: https://github.com/gugu9999gu/leerness/issues
457
- - PRs: e2e 통과 + 한국어 주석 + UTF-8 (no BOM) / pass e2e, Korean comments, UTF-8 without BOM.
458
- - 테스트 / Tests:
459
- ```bash
460
- npm run test:fast # 빠른 핵심-경로 smoke (selftest + 13 체크, ~10초) / fast core-path smoke (~10s)
461
- npm test # 전체 게이트 (selftest + e2e 220+) / full gate
462
- ```
148
+ ## 링크
463
149
 
464
- ## 📄 License
150
+ - 홈페이지: https://leerness.com
151
+ - npm: https://www.npmjs.com/package/leerness
152
+ - GitHub: https://github.com/gugu9999gu/leerness
153
+ - 변경 이력: [CHANGELOG.md](./CHANGELOG.md)
465
154
 
466
- MIT — © leerness contributors
155
+ ## 라이선스
467
156
 
468
- > **AI 에이전트가 신뢰할 수 있게 일하도록 만드는 도구. 사용자 동의 없이 외부 LLM/API/CLI를 자동 호출하지 않습니다.**
469
- > **A tool that makes AI agents work reliably. It never auto-calls external LLMs/APIs/CLIs without your consent.**
157
+ MIT
470
158
 
471
159
  <!-- leerness:project-readme:start -->
472
160
  ## Leerness Project Harness
473
161
 
474
- 이 프로젝트는 Leerness v1.9.439 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
162
+ 이 프로젝트는 Leerness v1.9.440 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
475
163
 
476
164
  ### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
477
165
 
@@ -525,7 +213,7 @@ leerness memory restore decision <date|title>
525
213
 
526
214
  ### MCP server (외부 AI 통합)
527
215
 
528
- Leerness v1.9.439는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
216
+ Leerness v1.9.440는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
529
217
 
530
218
  ```jsonc
531
219
  // 카테고리별
@@ -546,7 +234,7 @@ Leerness v1.9.439는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
546
234
  `<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
547
235
  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) 다음 라운드 예약.
548
236
 
549
- 현재 누적: **70 라운드 (1.9.40 → 1.9.439)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
237
+ 현재 누적: **70 라운드 (1.9.40 → 1.9.440)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
550
238
 
551
239
  ### 성능 가이드 (1.9.140 측정)
552
240
 
@@ -584,6 +272,5 @@ leerness release pack --close --auto-main-push
584
272
  - `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
585
273
  - `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
586
274
 
587
- Last synced by Leerness v1.9.439: 2026-06-07
275
+ Last synced by Leerness v1.9.440: 2026-06-08
588
276
  <!-- leerness:project-readme:end -->
589
-
package/bin/leerness.js CHANGED
@@ -31,7 +31,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
31
31
  // 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
32
32
  const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_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 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
33
33
 
34
- const VERSION = '1.9.439';
34
+ const VERSION = '1.9.440';
35
35
 
36
36
  // 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
37
37
  // 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
@@ -3298,6 +3298,15 @@ function _selfTestCases() {
3298
3298
  const jsonStillLog = modSrc.includes('log(JSON.stringify({ root, score'); // 최종 JSON 출력은 log 유지
3299
3299
  return afLogDef && usesAfLog && jsonStillLog;
3300
3300
  } },
3301
+ { name: '12th 외부평가 Opus P2 (UR-0140 stab): 시크릿 스캐너 prefix 패턴도 placeholder 가드 적용 (1.9.440)', run: () => {
3302
+ const src = read(__filename);
3303
+ // 스캐너가 valueGroup 없을 때 m[0] 로 placeholder 판정(prefix 패턴 가드)
3304
+ const wired = src.includes('const val = (valueGroup != null) ? m[valueGroup] : m[0];') && src.includes('if (_isPlaceholderSecret(val)) {');
3305
+ // 행위: prefix 8연속 더미는 placeholder, 진짜(고엔트로피)는 real (FN 정책 유지 — 'example' 포함 실키는 real)
3306
+ const m = require('../lib/pure-utils');
3307
+ const behav = m._isPlaceholderSecret('AKIA' + 'X'.repeat(16)) === true && m._isPlaceholderSecret('AKIAJQXMP7RZ2KL9WXYZ') === false && m._isPlaceholderSecret('sk-EXAMPLEab12cd34ef56gh78ij90kl') === false;
3308
+ return wired && behav;
3309
+ } },
3301
3310
  { name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
3302
3311
  ];
3303
3312
  }
@@ -7123,11 +7132,11 @@ function _collectSecretFindings(root) {
7123
7132
  let m;
7124
7133
  while ((m = re.exec(text))) {
7125
7134
  // 1.9.365 (CV-6/UR-0081): placeholder/예시 값 스킵.
7126
- if (valueGroup != null) {
7127
- const val = m[valueGroup];
7128
- if (_isPlaceholderSecret(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
7129
- if (requireSecretLike && !_looksSecretLike(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
7130
- }
7135
+ // 1.9.440 (12th 외부평가 Opus P2): prefix 패턴(valueGroup 없음, AWS/GitHub 등 19종) placeholder 가드 적용 —
7136
+ // 기존엔 valueGroup 있는 2패턴만 가드 → .env.example 의 더미 토큰(AKIAXXXX…/ghp_XXXX…)이 커밋 시크릿 오탐(CI 파손). 전체 매치(m[0])로 판정.
7137
+ const val = (valueGroup != null) ? m[valueGroup] : m[0];
7138
+ if (_isPlaceholderSecret(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
7139
+ if (valueGroup != null && requireSecretLike && !_looksSecretLike(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
7131
7140
  const line = text.slice(0, m.index).split('\n').length;
7132
7141
  findings.push({ file: fileRel, line, name, snippet: m[0].slice(0, 32), gitignored });
7133
7142
  break;
package/lib/pure-utils.js CHANGED
@@ -636,6 +636,7 @@ function _isPlaceholderSecret(value) {
636
636
  const hasRealPrefix = /^(?:sk-|sk-proj-|pk_|rk_|akia|ghp_|gho_|ghs_|ghr_|github_pat_|xox[baprs]-|aiza|ya29\.|glpat-|-----begin)/.test(v);
637
637
  // 1.9.436 (11th 외부평가 Opus P3): prefix 가 있어도 본문이 동일문자 8+연속(AKIAXXXX…/…00000000…)이면 명백한 더미 → placeholder. 실키는 고엔트로피라 무영향.
638
638
  if (/(.)\1{7,}/.test(alnum)) return true;
639
+ // (12th 외부평가 Opus P3 'AWS …EXAMPLE 키'는 보류 — 'example' 통째 placeholder 화는 기존 FN 정책(sk-EXAMPLE… 실키 유지, UR-0105)과 충돌. UR-0144 로 분리.)
639
640
  // 실키 prefix → 항상 실키(마커 무시). 그 외 마커 단어 있으면 placeholder(고엔트로피여도). prefix 없고 마커 없고 고엔트로피 → 실키.
640
641
  if (hasRealPrefix) return false;
641
642
  if (hasMarker) return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.439",
3
+ "version": "1.9.440",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -37,7 +37,7 @@ run('scan secrets', ['scan', 'secrets', tmp]);
37
37
  run('encoding check', ['encoding', 'check', tmp]);
38
38
 
39
39
  const secretFile = path.join(tmp, 'fake-config.json');
40
- fs.writeFileSync(secretFile, JSON.stringify({ openai: 'sk-' + 'A'.repeat(48) }));
40
+ fs.writeFileSync(secretFile, JSON.stringify({ openai: 'sk-' + 'a1B2'.repeat(12) }));
41
41
  run('scan secrets (detect)', ['scan', 'secrets', tmp], { expectFail: true });
42
42
  fs.unlinkSync(secretFile);
43
43
 
@@ -2695,7 +2695,7 @@ total++;
2695
2695
  total++;
2696
2696
  {
2697
2697
  fs.mkdirSync(path.join(tmp, '_devspace'), { recursive: true });
2698
- fs.writeFileSync(path.join(tmp, '_devspace/secret-config.js'), `const k = "ghp_${'a'.repeat(36)}";\n`);
2698
+ fs.writeFileSync(path.join(tmp, '_devspace/secret-config.js'), `const k = "ghp_${'a1B2'.repeat(9)}";\n`);
2699
2699
  fs.writeFileSync(path.join(tmp, '.leerness-skip-dirs'), '_devspace/\n# 주석은 무시\n');
2700
2700
  const r = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', tmp], { encoding: 'utf8' });
2701
2701
  const ok = r.status === 0 && /no obvious secret patterns/.test(r.stdout);
@@ -3650,7 +3650,7 @@ total++;
3650
3650
  let ok = false;
3651
3651
  try {
3652
3652
  const sd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-secmod-'));
3653
- const A = 'A'.repeat(40);
3653
+ const A = 'a1B2'.repeat(10);
3654
3654
  const lines = [
3655
3655
  'const a = "' + 'sk-' + 'proj-' + A + '_' + A + '";', // modern OpenAI project (기존 패턴 놓침)
3656
3656
  'const b = "' + 'sk-' + 'ant-api03-' + A + '_' + A + '";', // Anthropic api03 (언더스코어 — 기존 놓침)
@@ -4434,7 +4434,7 @@ total++;
4434
4434
  let ok = false;
4435
4435
  try {
4436
4436
  const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
4437
- const A = 'A'.repeat(40);
4437
+ const A = 'a1B2'.repeat(10);
4438
4438
  const hit = (s) => c.SECRET_PATTERNS.some(p => { p.re.lastIndex = 0; return p.re.test(s); });
4439
4439
  const catOk = Array.isArray(c.SECRET_PATTERNS) && c.SECRET_PATTERNS.length === 20
4440
4440
  && hit('AKIA' + 'ABCD1234EFGH5678') && hit('sk-' + 'ant-api03-' + A + '_' + A) && !hit('const u = "john' + '_doe_2024";');
@@ -4634,7 +4634,7 @@ total++;
4634
4634
  fs.rmSync(skf, { recursive: true, force: true });
4635
4635
  // UR-0060: 사용자 harness.js 파일도 스캔(false-neg 제거) + 신규 GitLab 패턴 탐지
4636
4636
  const ud = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-scan-'));
4637
- fs.writeFileSync(path.join(ud, 'harness.js'), 'const k = "glpat-' + 'x'.repeat(20) + '";\n');
4637
+ fs.writeFileSync(path.join(ud, 'harness.js'), 'const k = "glpat-' + 'a1B2'.repeat(5) + '";\n');
4638
4638
  const scr = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', ud], { encoding: 'utf8', timeout: 20000 });
4639
4639
  const scanOk = /GitLab PAT/.test((scr.stdout || '') + (scr.stderr || ''));
4640
4640
  fs.rmSync(ud, { recursive: true, force: true });
@@ -4736,7 +4736,7 @@ total++;
4736
4736
  // scan secrets <file> (이전 ENOTDIR) + basename 표시
4737
4737
  const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-p3-'));
4738
4738
  cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
4739
- fs.writeFileSync(path.join(d, 'leak.js'), 'const k = "glpat-' + 'x'.repeat(20) + '";\n');
4739
+ fs.writeFileSync(path.join(d, 'leak.js'), 'const k = "glpat-' + 'a1B2'.repeat(5) + '";\n');
4740
4740
  const fr = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', path.join(d, 'leak.js')], { encoding: 'utf8', timeout: 20000 });
4741
4741
  const fout = (fr.stdout || '') + (fr.stderr || '');
4742
4742
  const fileScanOk = /GitLab PAT/.test(fout) && /leak\.js/.test(fout) && !/ENOTDIR/.test(fout);
@@ -6091,5 +6091,25 @@ total++;
6091
6091
  if (!ok) failed++;
6092
6092
  }
6093
6093
 
6094
+ // 1.9.440 (12th 외부평가 Opus P2): 시크릿 스캐너 prefix 패턴(AWS/GitHub)도 placeholder 가드 — .env.example 더미는 미탐, 진짜는 탐지.
6095
+ total++;
6096
+ {
6097
+ let ok = false;
6098
+ try {
6099
+ const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-scanph-'));
6100
+ cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
6101
+ // 더미 prefix 토큰(.env.example, gitignore 대상 아님) → 미탐(exit 0)
6102
+ fs.writeFileSync(path.join(d, '.env.example'), 'AWS_KEY=AKIA' + 'X'.repeat(16) + '\nGH=ghp_' + 'X'.repeat(36) + '\n');
6103
+ const dummy = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', d], { encoding: 'utf8', timeout: 20000 });
6104
+ // 진짜 AWS 키(AKIA+16 랜덤) → 탐지(exit 1)
6105
+ fs.writeFileSync(path.join(d, 'real.js'), 'const k="AKIAJQXMP7RZ2KL9WXYZ";');
6106
+ const real = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', d], { encoding: 'utf8', timeout: 20000 });
6107
+ fs.rmSync(d, { recursive: true, force: true });
6108
+ ok = dummy.status === 0 && real.status === 1;
6109
+ } catch {}
6110
+ console.log(ok ? '✓ B(1.9.440) UR-0140: 시크릿 스캐너 prefix 더미 미탐 + 진짜 탐지(placeholder 가드 통합)' : '✗ 시크릿 스캐너 prefix 가드 실패');
6111
+ if (!ok) failed++;
6112
+ }
6113
+
6094
6114
  console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
6095
6115
  if (failed > 0) process.exit(1);