export type SolutionStatus = 'experiment' | 'candidate' | 'verified' | 'mature' | 'retired'; export type SolutionType = 'pattern' | 'solution' | 'decision' | 'troubleshoot' | 'anti-pattern' | 'convention'; export interface SolutionEvidence { injected: number; reflected: number; negative: number; sessions: number; reExtracted: number; } export interface SolutionFrontmatter { name: string; version: number; status: SolutionStatus; confidence: number; type: SolutionType; scope: 'me' | 'team' | 'project' | 'universal'; tags: string[]; identifiers: string[]; evidence: SolutionEvidence; created: string; updated: string; supersedes: string | null; extractedBy: 'auto' | 'manual'; } export interface SolutionV3 { frontmatter: SolutionFrontmatter; context: string; content: string; filePath?: string; } export interface SolutionIndexEntry { name: string; status: SolutionStatus; confidence: number; type: SolutionType; scope: 'me' | 'team' | 'project' | 'universal'; tags: string[]; /** * Pre-expanded tag set, computed at index build time via the term normalizer. * Contains `tags` plus every related match term from the canonical families * they belong to. * * T2 scope (current): populated as forward-looking metadata for T3's * ranking-decision log (which records raw + normalized query/solution * terms for offline explainability) and T4's BM25 term-frequency stats. * **NOT consumed by the current matcher** — `rankCandidates` and * `calculateRelevance` still use raw `tags` for intersection to preserve * the Round 3 baseline (bidirectional expansion would inflate recall 5-10× * and invalidate fixture metrics). A future PR that uses `normalizedTags` * in scoring must update `ROUND3_BASELINE` in the same commit. * * Not persisted — recomputed on every index build. Safe to regenerate whenever * `DEFAULT_MATCH_TERMS` changes. */ normalizedTags: string[]; identifiers: string[]; filePath: string; } export declare const DEFAULT_EVIDENCE: SolutionEvidence; export declare function slugify(text: string): string; /** Runtime type guard for SolutionFrontmatter */ export declare function validateFrontmatter(fm: unknown): fm is SolutionFrontmatter; /** * Return a list of validation errors for a parsed frontmatter object. * * Empty array = valid. Non-empty = each entry describes one missing/wrong * field. Callers that only need a boolean should use `validateFrontmatter`. * Slow path (quarantine logging) uses this to produce actionable diagnostics. */ export declare function diagnoseFrontmatter(fm: unknown): string[]; /** Parse YAML frontmatter from solution file content */ export declare function parseFrontmatterOnly(content: string): SolutionFrontmatter | null; /** Parse a full V3 solution file into its components */ export declare function parseSolutionV3(content: string): SolutionV3 | null; /** Serialize a SolutionV3 to a markdown string with YAML frontmatter */ export declare function serializeSolutionV3(solution: SolutionV3): string; /** Check if content is in V1 format (# Title + > Type: pattern) */ export declare function isV1Format(content: string): boolean; /** 한국어 일반 조사/어미 — strip 대상 (긴 것부터 매칭) * * term-matcher에서 재사용 가능하도록 export — 매칭 시점과 추출 시점의 stripping * 규칙을 단일 source of truth로 유지해 한국어 stem 비교 정합성 보장. * * 주의: 이 리스트는 **추출 시점에도 적용**되므로 1글자 suffix를 추가할 때 * `집중`→`집`, `시도`→`시` 같은 한자어 명사가 깨지지 않도록 극도로 보수적으로 * 유지한다. 동사 활용형(`리팩토링중`, `배포시`)처럼 매칭 전용 suffix가 필요하면 * term-matcher의 `KO_VERBAL_SUFFIXES`에 따로 둔다. */ export declare const KO_SUFFIXES: string[]; export declare function stripKoSuffix(word: string): string; /** * Extract tags from text. * Korean 2-char words preserved (e.g. "에러", "배포"), stopwords filtered. * English words require 3+ chars, stopwords filtered. * Tags capped at MAX_TAGS, ranked by frequency. * * NOTE on hyphens: this function strips `-` to a space (`api-key` query token * becomes `api` and `key` separately). Solution-side compound tags are * recovered downstream by `expandCompoundTags`, and query-side bigram * recovery is done by `expandQueryBigrams`. Both ship as part of R4-T1 * (compound-tag tokenizer fix) — see `docs/plans/2026-04-08-t4-bm25-skip-adr.md` * "Round 4 candidates" section for the rationale. Changing this regex * directly was considered but rejected: it would silently shift the index * representation of every existing solution, requiring an index rebuild and * a fresh `ROUND3_BASELINE` measurement on every downstream PR. */ export declare function extractTags(text: string): string[]; /** * Expand a solution tag list with hyphen-split alternatives. * * Each input tag is preserved verbatim, and any tag containing `-` also * contributes its parts (length ≥ 3 each) as additional tags. The output * is deduplicated. * * Examples: * - `['api-key', 'security']` → `['api-key', 'api', 'key', 'security']` * - `['code-review', 'quality']` → `['code-review', 'code', 'review', 'quality']` * - `['n+1', 'database']` → `['n+1', 'database']` (no hyphen, n+1 unchanged) * - `['red-green-refactor']` → `['red-green-refactor', 'red', 'green', 'refactor']` * - `['typescript']` → `['typescript']` (no hyphen, no expansion) * * Korean compound tags (`API에러`, `테스트주도개발`) are preserved verbatim * because they contain no `-`. The expansion is intentionally English- * compound-aware only — Korean compound recovery is not in scope for R4-T1 * (the existing `term-normalizer` family expansion handles Korean ↔ English * cross-mapping). * * The output ordering is insertion order: original tags first, then split * parts in left-to-right order. Stable across runs (Set + Array dedup). */ export declare function expandCompoundTags(tags: readonly string[]): string[]; /** * Expand a query tag list with adjacent-token bigram alternatives. * * For each adjacent (a, b) pair where both tokens are length ≥ 3, the * function adds: * - `a-b` (hyphen-joined form, e.g. `api-key`) * - `ab` (concatenated form, e.g. `apikey`) * - `a-b'` (singular stem of b, only if b ends in `s` and length > 3) * - `ab'` (concatenated singular stem) * * Examples: * - `['api', 'keys']` → `['api', 'keys', 'api-key', 'apikey', 'api-keys', 'apikeys']` * - `['code', 'review']` → `['code', 'review', 'code-review', 'codereview']` * - `['red', 'green', 'refactor']` → `[..., 'red-green', 'redgreen', 'green-refactor', 'greenrefactor']` * * Plural→singular stem is intentionally minimal: only `s`-suffix removal, * no `es`/`ies` handling. The cost-benefit is asymmetric — `apis → api` * is the highest-value case and is handled correctly; `classes → classe` * is wrong but doesn't matter because no solution tag is `classe`. * * Why both `-` and concatenated forms: solution tag conventions vary * across packs (`api-key` vs `apikey`), and this expansion is cheap. * The downstream intersection check is O(M) per solution where M = expanded * query tag count, so even doubling the query tag count is well within * the matcher's hot-path budget for the corpus sizes Forgen targets * (N ≤ 200 solutions). * * Korean tokens (`/[가-힣]/`) are passed through verbatim: bigram * concatenation of Korean compound words is meaningless because the * boundary is lexical, not whitespace-driven (`디버깅` is one word, not * two adjacent tokens). Only ASCII-letter pairs participate. */ export declare function expandQueryBigrams(tags: readonly string[]): string[]; export declare function expandQueryKoreanStems(tags: readonly string[]): string[]; /** Migrate a V1-format solution file to V3 format */ export declare function migrateV1toV3(content: string, filePath: string): string;