leerness 1.36.13 → 1.36.14

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.36.14 — 2026-07-13 — database 렌즈 10→11문항: 37-코퍼스 검증 매트릭스가 측정한 커버리지 갭(hot row/부하 계열) 보강
4
+
5
+ - **왜(사용자 지시)**: leerness 설치 프로젝트(lens-validation, 신규)에서 로컬 SQLite 로 37개 코퍼스 요소를 14개 실행형 픽스처로 재현하고, 요소별 렌즈 작동을 행위 테스트(설치된 CLI 로 task→done→verify-claim 트리거 + lens 실렌더 문항 근거)로 측정 — 트리거 14/14 ✓·음성 대조(비-DB 파일) 무발화 ✓·redis-only 캐시 파일 트리거 ✓(_DB_IMPORT_RE 의 redis/ioredis). 유일한 측정 갭: **hot row·sharded counter·큐 직렬화·backpressure·bulkhead 5개 요소를 다루는 문항이 0개**.
6
+ - **추가 11번(KO+EN)**: hot row 락 경합의 전파(대기→타임아웃→데드락→풀 고갈), 비정확 카운터의 샤딩(+재고/잔액엔 샤딩 금지 주의), 같은 키 경합의 큐 직렬화·예약 패턴(expires_at 회수), 유입>처리량 시 backpressure(큐 상한·rate limit·워커/풀 상한·bulkhead).
7
+ - **selftest**: database 렌즈 케이스 11문항(KO=EN) + hot row/backpressure/bulkhead 앵커(EN 동형) + 병합 cap(12) 무절단 가드 갱신.
8
+ - **검증**: selftest 286/286 + 매트릭스 재실행으로 f13 클러스터 '작동' 전환 확인(게시 후 lens-validation 에서 재실증). 정직 한계: 렌즈는 advisory 자기질문이며, 문항 존재 = 자동 감지가 아니다.
9
+
3
10
  ## 1.36.13 — 2026-07-13 — database 렌즈 8→10문항: 9라운드 dogfood playbook 역이식
4
11
 
5
12
  - **왜**: stock-service 에서 코퍼스 9개 동시성 개념을 실구현-리뷰-재현 루프로 dogfood 한 결과(회고: db-safety-lens/review-playbook.md), 기존 8문항이 다루지 않는 결함 패밀리가 반복 확인됨 — 전부 "그린 테스트는 통과했는데 리뷰가 잡은" 실사건 기반.
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.13 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
128
+ 이 프로젝트는 Leerness v1.36.14 하네스를 사용합니다. 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.13는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **86개 도구**를 노출:
182
+ Leerness v1.36.14는 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.13는 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.13)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
203
+ 현재 누적: **70 라운드 (1.9.40 → 1.36.14)** · 매 라운드 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.13: 2026-07-13
241
+ Last synced by Leerness v1.36.14: 2026-07-13
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.13';
36
+ const VERSION = '1.36.14';
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') 시 호스트 프로세스 오염.
@@ -3894,17 +3894,19 @@ function _selfTestCases() {
3894
3894
  && eq(F([]), [])
3895
3895
  && eq(F(['a.css', 'b.md', 'c.js', 'd.test.js']).length, 2); // 최대 2개 클러터 방지
3896
3896
  } },
3897
- { name: 'DB 안전성 렌즈 (코어 승격 + 9라운드 playbook 역이식): database 도메인 10문항(KO=EN) + DB파일 매핑 인라인 + 비-DB 회귀0 (행위)', run: () => {
3897
+ { name: 'DB 안전성 렌즈 (코어 승격 + playbook 역이식 + 37코퍼스 매트릭스 갭 보강): database 도메인 11문항(KO=EN) + DB파일 매핑 인라인 + 비-DB 회귀0 (행위)', run: () => {
3898
3898
  const F = _lensDomainsForFiles;
3899
3899
  const eq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
3900
3900
  const d = LENS_CATALOG.database;
3901
- const catOk = !!d && d.questions.length === 10 && d.questionsEn.length === 10
3901
+ const catOk = !!d && d.questions.length === 11 && d.questionsEn.length === 11
3902
3902
  && d.questions[0].includes('lost update') && d.questions.some(q => q.includes('TOCTOU'))
3903
3903
  && d.questions.some(q => q.includes('in-doubt')) && d.questions.some(q => q.includes('SKIP LOCKED'))
3904
3904
  && d.questions.some(q => q.includes('thenable')) // playbook 역이식 9번(콜백 async 이탈+등록/abort 장벽)
3905
3905
  && d.questions.some(q => q.includes('ABA')) // playbook 역이식 10번(전-라이터 참여+토큰 식별자+테스트 정직성)
3906
+ && d.questions.some(q => q.includes('hot row') && q.includes('backpressure') && q.includes('bulkhead')) // 11번(37코퍼스 매트릭스 갭: hot row/샤딩/큐 직렬화/backpressure/bulkhead)
3906
3907
  && d.questionsEn.some(q => q.includes('thenables')) && d.questionsEn.some(q => q.includes('ABA'))
3907
- && _mergeLensCatalog(LENS_CATALOG, {}).database.questions.length === 10 // 병합 cap 이 내장 10문항을 자르지 않음 (cap 8 회귀 방지)
3908
+ && d.questionsEn.some(q => q.includes('hot row') && q.includes('bulkhead'))
3909
+ && _mergeLensCatalog(LENS_CATALOG, {}).database.questions.length === 11 // 병합 cap(12)이 내장 11문항을 자르지 않음
3908
3910
  && d.affects.every(a => LENS_CATALOG[a]);
3909
3911
  // DB 파일 → database 최우선(questions[0] 인라인 노출), DB+code → [database, code], 비-DB 코드 → 회귀 없음
3910
3912
  const mapOk = eq(F(['db/migrations/001_init.sql']), ['database'])
@@ -4917,7 +4919,9 @@ const LENS_CATALOG = {
4917
4919
  "동시성 방어를 인프로세스 뮤텍스·싱글턴·인메모리 dedup에 기대거나, 여러 서비스에 걸친 작업을 하나의 DB 트랜잭션으로 감쌀 수 있다고 착각하거나, 방금 프라이머리에 쓴 값을 지연된 리드 레플리카에서 읽고 있지 않은가? — 인스턴스가 2개 이상이면 인프로세스 락은 무력하니 공유 지점으로 옮기고(DB UNIQUE 제약·행 잠금·advisory lock, 큐 소비는 FOR UPDATE SKIP LOCKED), 원자성은 프로세스/네트워크 경계를 못 넘으니 크로스서비스는 아웃박스+saga+보상 트랜잭션으로, at-least-once 전달은 멱등키(UNIQUE)로, 복제 지연이 깨는 read-your-writes는 프라이머리 라우팅/LSN 확인으로 옮겼는가? 분산 락을 쓴다면 리스 만료 후 늦게 깨어난 작업이 최신 작업을 덮어쓰지 못하게 단조 증가 펜싱 토큰(DB 시퀀스/행 버전)을 발급하고 하위 자원이 낮은 토큰을 거부하게 했는가?",
4918
4920
  // 9~10번: 9-라운드 현장 dogfood 회고(review-playbook)에서 역이식 — 그린 테스트가 놓치고 리뷰가 잡은 결함 패밀리.
4919
4921
  "트랜잭션·락·리스·single-flight 가 감싼 '사용자 콜백'이 promise 를 반환할 수 있지 않은가? — async 콜백은 첫 suspension 까지만 동기라, await 하지 않으면 그 뒤 continuation 이 COMMIT/해제 뒤 보호 밖에서 실행된다(아웃박스 릴레이가 async 발행 실패를 모른 채 sent=1로 마킹, 펜스 해제 뒤 늦은 쓰기). await 를 설계로 강제하거나 thenable 을 명시적으로 거부했는가? 진행 중 작업을 추적하는 구조(단일비행 맵·락 테이블·in-progress 레지스트리)는 사용자 코드가 실행되거나 동기 throw 하기 '전'에 등록을 마치는가 — 등록 전에 콜백이 던지면 실패 경로가 반초기화 상태를 보고 원본 에러가 삼켜지거나 죽은 엔트리가 키를 영구 오염시킨다. abort/해제는 그 작업의 흔적 전부(보유 락·대기열 엔트리·무효화 표식·세대 카운터)를 걷어내는 장벽인가 — 걷다 만 대기열 엔트리는 죽은 작업에 락을 승계시키고, 세대 재검사 없는 중단된 획득 루프는 abort 뒤에도 계속 획득한다. 그리고 정리는 identity 가드(맵의 엔트리가 여전히 '내 것'일 때만 삭제)로 후속 작업의 엔트리를 지우지 않는가?",
4920
- "버전 컬럼·단조 토큰·워터마크가 지키는 불변식에 '모든' 라이터가 참여하는가 — upsert·마이그레이션·시드·관리자 경로 중 하나만 우회해도(INSERT OR REPLACE 가 version 을 기본값으로 초기화, 평범한 대입이 토큰을 되감음) 낡은 스냅샷의 CAS 가 통과한다(ABA). 그 토큰/키는 식별자를 갖는가 — 클러스터/스토어 표식 없는 맨 숫자 LSN 은 무관한 스토어의 레플리카와도 '같음'으로 비교돼 남의 데이터를 노출하고, 객체 키는 identity 비교라 같은 내용의 {id:1}도 서로 다른 키다. 수량/금액 파라미터는 경계에서 검증되는가 — 음수 qty 는 stock >= qty 가드를 '통과'하면서 이체 방향을 뒤집는다(역방향 절도). 마지막으로 그 방어의 테스트가 구현에 아부하지 않는가 — 구현이 스스로 보고하는 라벨(source:'replica')이 아니라 분기된 데이터로 판별하고, no-op/naive 변이를 실제로 돌려 그 테스트가 '실패함'을 확인했으며, '언젠가 완료/거부된다'류 활성성 검증엔 timeout 이 있어 감지기가 무력화된 변이에서 스위트가 행(hang)이 아니라 실패로 끝나는가?"
4922
+ "버전 컬럼·단조 토큰·워터마크가 지키는 불변식에 '모든' 라이터가 참여하는가 — upsert·마이그레이션·시드·관리자 경로 중 하나만 우회해도(INSERT OR REPLACE 가 version 을 기본값으로 초기화, 평범한 대입이 토큰을 되감음) 낡은 스냅샷의 CAS 가 통과한다(ABA). 그 토큰/키는 식별자를 갖는가 — 클러스터/스토어 표식 없는 맨 숫자 LSN 은 무관한 스토어의 레플리카와도 '같음'으로 비교돼 남의 데이터를 노출하고, 객체 키는 identity 비교라 같은 내용의 {id:1}도 서로 다른 키다. 수량/금액 파라미터는 경계에서 검증되는가 — 음수 qty 는 stock >= qty 가드를 '통과'하면서 이체 방향을 뒤집는다(역방향 절도). 마지막으로 그 방어의 테스트가 구현에 아부하지 않는가 — 구현이 스스로 보고하는 라벨(source:'replica')이 아니라 분기된 데이터로 판별하고, no-op/naive 변이를 실제로 돌려 그 테스트가 '실패함'을 확인했으며, '언젠가 완료/거부된다'류 활성성 검증엔 timeout 이 있어 감지기가 무력화된 변이에서 스위트가 행(hang)이 아니라 실패로 끝나는가?",
4923
+ // 11번: 37-코퍼스 렌즈 검증 매트릭스(lens-validation, 2026-07)가 측정한 커버리지 갭 — hot row/부하 계열.
4924
+ "쓰기가 한 행/키에 몰리지 않는가(인기 상품 재고·전역 카운터·선착순 쿠폰 — hot row)? — 단일 행 락 경합은 대기·타임아웃·데드락·커넥션 풀 고갈로 번진다. 정확한 실시간 값이 필요 없는 카운터(좋아요·조회수)는 샤딩(counter shard N개에 분산 후 합산)으로 흩고 — 단 정확성이 필요한 재고/잔액 차감엔 샤딩을 함부로 쓰지 마라 —, 경합이 심한 같은 키 작업은 큐 직렬화(같은 키 → 같은 파티션 순차 처리)나 예약 패턴(임시 점유 + expires_at 만료 회수)으로 경합 자체를 줄였는가? 유입이 처리량을 넘어설 때의 backpressure 설계가 있는가 — 큐 상한·rate limit·워커/커넥션 풀 상한·기능별 풀 분리(bulkhead)로 흐름을 제어하지 않고 DB 락만으로 폭주를 받으면 장애가 시스템 전체로 전파된다."
4921
4925
  ],
4922
4926
  questionsEn: [
4923
4927
  "Am I reading a value with SELECT, computing it in app code, then writing it back with UPDATE? Two requests reading the same row concurrently silently drop one write (lost update). Did I switch to an atomic conditional UPDATE (SET n = n + ?) or a version CAS (... WHERE version = ?, treating 0 rows affected as a conflict to retry, not as success)?",
@@ -4929,7 +4933,8 @@ const LENS_CATALOG = {
4929
4933
  "Did the write I returned 'success' for actually reach a checked COMMIT (did I await the commit and inspect its result), and does that ack mean stable storage — WAL/redo/journal flushed on commit — not just a memory buffer? With autocommit off and a forgotten COMMIT, the open transaction is silently rolled back when the connection returns to the pool. Durability settings differ by engine — did I verify the one matching this data's class (e.g. Postgres synchronous_commit, MySQL innodb_flush_log_at_trx_commit=1 + sync_binlog=1, SQLite synchronous=FULL; Redis is not durable by default)? For must-survive-node-loss data, did I require a quorum / synchronous-replication ack (e.g. w:majority, synchronous standby) so one node dying can't lose it, and state the failover RPO?",
4930
4934
  "Am I relying on an in-process mutex/singleton/in-memory dedup, or assuming one DB transaction can wrap work spanning multiple services, or reading a just-written value from a lagging read replica? With >=2 instances an in-process lock is useless, so move mutual exclusion to a shared point (DB UNIQUE constraint, row lock, advisory lock; pull queue work with FOR UPDATE SKIP LOCKED); atomicity can't cross a process/network boundary, so route cross-service work through an outbox + saga + compensating actions and collapse at-least-once delivery with an idempotency key (UNIQUE); and replication lag breaks read-your-writes, so route read-after-write / consistency-critical reads to the primary or gate on replica LSN/lag. If I use a distributed lock, did I issue a monotonically increasing fencing token (DB sequence / row version) and have the guarded resource reject lower tokens, so a worker that resumes after its lease expired can't overwrite newer work?",
4931
4935
  "Can a user callback wrapped by a transaction/lock/lease/single-flight return a promise? An async callback is synchronous only until its first suspension — if the returned promise isn't awaited, its continuations run after COMMIT/release, outside the protection (an outbox relay marking sent=1 before an async publish rejects; a fenced write landing after release). Did I await by design, or explicitly reject thenables? Does every 'track this operation' structure (single-flight map, lock table, in-progress registry) finish REGISTERING before user code can run or throw synchronously — a callback that throws pre-registration leaves the failure path staring at half-initialized state (the real error swallowed, a dead entry poisoning the key forever)? Is abort/release a real barrier that removes EVERY trace of the operation (held locks, queue entries, invalidation flags, generation counters) — a leftover queue entry hands the lock to a dead transaction, and a suspended acquisition loop without a post-await generation re-check keeps acquiring after the abort. And does cleanup use an identity guard (delete only if the map entry is still MINE) so it never removes a successor's entry?",
4932
- "Does EVERY writer participate in the invariant that a version column / monotonic token / watermark protects — one bypassing path (an INSERT OR REPLACE resetting version to its default, a plain assignment rolling a token backward, a migration/seed/admin write) lets a stale snapshot's CAS pass (ABA). Does the token/key carry identity — a bare numeric LSN with no cluster/store branding compares 'equal' against an unrelated store's replica and exposes foreign data, and object keys compare by identity so two {id:1} objects are different keys. Are quantity/amount parameters validated at the boundary — a negative qty PASSES a stock >= qty guard while inverting the transfer's direction (reverse theft). Finally, do the tests for these defenses avoid flattering the implementation — assert on divergent data rather than a self-reported label (source:'replica'), actually run the no-op/naive mutant and watch the test FAIL it, and put timeouts on 'eventually completes/rejects' liveness checks so a disabled detector fails the suite instead of hanging it?"
4936
+ "Does EVERY writer participate in the invariant that a version column / monotonic token / watermark protects — one bypassing path (an INSERT OR REPLACE resetting version to its default, a plain assignment rolling a token backward, a migration/seed/admin write) lets a stale snapshot's CAS pass (ABA). Does the token/key carry identity — a bare numeric LSN with no cluster/store branding compares 'equal' against an unrelated store's replica and exposes foreign data, and object keys compare by identity so two {id:1} objects are different keys. Are quantity/amount parameters validated at the boundary — a negative qty PASSES a stock >= qty guard while inverting the transfer's direction (reverse theft). Finally, do the tests for these defenses avoid flattering the implementation — assert on divergent data rather than a self-reported label (source:'replica'), actually run the no-op/naive mutant and watch the test FAIL it, and put timeouts on 'eventually completes/rejects' liveness checks so a disabled detector fails the suite instead of hanging it?",
4937
+ "Are writes converging on one hot row/key (a popular product's stock, a global counter, a first-come coupon)? Single-row lock contention cascades into waits, timeouts, deadlocks, and connection-pool exhaustion. For counters that don't need an exact real-time value (likes, views), spread the load with sharding (N counter-shard rows summed on read) — but don't casually shard stock/balance decrements that need exactness; for heavily contended same-key work, reduce the contention itself with queue-based serialization (same key → same partition, processed in order) or a reservation pattern (temporary hold + expires_at reclamation). And when inflow exceeds throughput, is backpressure designed in — queue bounds, rate limits, worker/connection-pool caps, per-feature pool isolation (bulkhead)? Absorbing a surge with DB locks alone propagates the failure system-wide."
4933
4938
  ],
4934
4939
  affects: ['code', 'test'], affectsNote: '동시성 방어엔 인터리빙/부하/재시도/멀티인스턴스 실패를 재현하는 테스트가 따라와야 함(단일 인스턴스 초록불은 증거가 아님)',
4935
4940
  affectsNoteEn: 'a concurrency defense needs an interleaving/load/retry/multi-instance reproduction test'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.13",
3
+ "version": "1.36.14",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",