#!/usr/bin/env node /** * Forgen — Solution Injector Hook * * Claude Code UserPromptSubmit 훅으로 등록. * 사용자 프롬프트에 관련된 축적 솔루션을 Claude 컨텍스트에 자동 주입합니다. * * knowledge-comes-to-you 원칙: 필요한 지식은 찾아와야 한다 */ /** * Minimum relevance thresholds by fitness state (2026-04-21 gate sweep). * * Motivation: a flat 0.3 floor gave 100% precision but 60% recall on a * synthetic 40-query workload — 10 legitimate matches that scored * 0.25-0.30 were blocked alongside noise. A pure 0.25 floor pushed recall * to 84% but stripped noise protection for unverified solutions. * * Champion-aware solution: trust graduates more. Solutions whose fitness * classification is `champion` or `active` (accept/correct ratio has * survived ≥5 injections under the v0.3.2 gates) earn a lower 0.25 * injection floor; everything else stays at 0.3. On the sweep this hit * precision 95.5% / recall 84% / off-topic specificity 100% — best * trade in the variant set. * * If fitness data is unavailable (fresh install, empty outcomes/), * every solution falls into the default 0.3 bucket — identical to the * pre-0.3.2 gate. No cold-start regression. * * W3-2 리뷰(SEV-3 #1): 0.3 리터럴을 relevance-gate.RELEVANCE_MATCH_GATE 로 단일화 — * 교정 클러스터링 τ 와 같은 소스를 공유해 근거-정직성 드리프트를 코드로 방지. */ export declare const MIN_INJECT_RELEVANCE = 0.3; export declare const MIN_INJECT_RELEVANCE_TRUSTED = 0.25; /** * v0.4.1 — cold-start 사용자 threshold. outcomes 이벤트가 거의 없는 신규 사용자 * 는 starter-pack 이 champion/active 로 승격될 기회가 없어 0.3 gate 에 막혀 주입 0. * 실측 (buyer-day1 v2): starter 15개 중 recall 5건 전부 relevance 0.08~0.25, 주입 0. * 이 값으로 첫날부터 매칭 가능성 제공 + 누적 후엔 표준 threshold 로 자연 전환. */ export declare const MIN_INJECT_RELEVANCE_COLD_START = 0.2; /** cold-start 판정 임계 — fitness state 있는 솔루션이 이 수 미만이면 신규 사용자 간주. */ export declare const COLD_START_FITNESS_THRESHOLD = 5; interface SessionCacheCommitResult { /** * commit 상태: * 'committed' — 정상적으로 lock 안에서 disk 갱신 완료 * 'lock-failed' — file lock 획득 실패 (stale recovery, timeout 등). disk는 변경 안 됨. * 'error' — lock은 잡았으나 parse/write 실패. disk 상태 불명확. * caller는 'lock-failed' 시 retry하거나 fail-open 처리해야 한다. */ status: 'committed' | 'lock-failed' | 'error'; /** * 이번 호출에서 disk에 실제로 새로 추가된 entries. * caller는 이 list로만 evidence.injected counter를 갱신해야 한다. * 다른 hook이 이미 같은 entry를 추가했다면 그 entry는 newlyAdded에 포함되지 않는다. */ newlyAdded: Array<{ name: string; chars: number; }>; /** * disk에 저장된 fresh totalInjectedChars. * status='committed'일 때만 정확한 값. 그 외엔 0 또는 fallback. */ totalInjectedChars: number; } /** * 새로 inject할 entries를 disk session cache에 commit한다. * * H-1 + M-3 fix: * - 이전 saveSessionCache는 caller의 메모리 set 전체를 저장 + Math.max로 chars 합산 * → disjoint write 합산 손실로 budget cap이 헐거워졌음 (H-1) * - 또한 두 hook이 거의 동시에 같은 솔루션을 inject 후보로 보면 둘 다 * evidence.injected를 증가시켜 중복 카운트 (M-3) * * 이번 fix: * 1. caller는 "이번에 추가하려는 entries (name+chars)"만 전달 * 2. lock 안에서 disk fresh를 읽어 이미 있는 name은 제외 * 3. 새로 추가된 것만 newlyAdded로 반환 * 4. disk의 fresh chars + newlyAdded chars를 합산해 새 total로 저장 * 5. caller는 newlyAdded로만 evidence.injected counter 갱신 → 중복 차단 */ /** * Test-only export: 격리된 회귀 테스트가 inline 재구현 대신 실 함수를 호출할 수 있도록 * 한다 (L-1 fix — PR2c-1 라운드 2 code-reviewer 발견). */ export declare function commitSessionCacheEntries(sessionId: string, newEntries: Array<{ name: string; chars: number; }>): SessionCacheCommitResult; interface TierSolution { name: string; type: string; confidence: number; matchedTags: string[]; } /** Tier 2 — 헤더 + 태그 + 핵심 최대 3줄 (최대 SUMMARY_MAX_CHARS) */ export declare function buildSummaryTier(sol: TierSolution, raw: string): string; /** * Tier 1 — 이름 + one-liner + compound-read MCP 힌트 (전문은 pull-on-demand). * type/confidence 헤더는 의도적으로 생략 — 인덱스 라인은 포인터일 뿐, 상세는 * compound-read로 당겨 읽는다. */ export declare function buildIndexTier(sol: TierSolution, raw: string): string; /** Tier 0 — 전문(Context+Content) 그대로. 파싱 실패/캡 초과면 null (호출부가 Tier 2로 폴백). */ export declare function buildFullTier(sol: TierSolution, raw: string): string | null; /** * 매치 목록(relevance desc 정렬 가정)을 Progressive Disclosure 텍스트로 렌더한다. * 순수 함수 — fs 읽기는 caller(main)가 `rawByName`으로 미리 채워 넣는다. * * - 매치가 정확히 1건 & 전문이 FULL_TEXT_MAX_CHARS 이하 → Tier 0(전문) * - 그 외 상위 TOP_TIER_COUNT건 → Tier 2(요약) * - 나머지 → Tier 1(인덱스 라인) */ export declare function renderSolutionTiers(sols: readonly TierSolution[], rawByName: ReadonlyMap): Map; export {};