배경
chosung: false 필드 옵션(멀티필드)은 초성-only 토큰을 해당 필드에서 hard filter로 gate-out한다 (matchFields.ts의 D4). 이 gate의 술어(isChosungOnlyToken)는 쿼리에 의존하므로, 타이핑 도중 모음이 붙는 순간(ㅍ → 파) 술어가 뒤집혀 필드가 un-gate되고 매치 집합이 커진다.
이는 라이브러리의 핵심 계약인 "타이핑할수록 결과 단조감소(monotonic narrowing)"를 UX 층위에서 깨뜨린다:
ㅍ → gate-out → 결과 없음
파 → un-gate → 결과 등장 (집합 증가)
- split 모드에선 모든 한글 단어가 초성으로 시작하므로, 새 토큰을 시작하는 매 키스트로크마다
chosung:false 필드에만 매치되는 아이템이 사라졌다 되살아나는 깜빡임 발생
issue #35의 세션 가드(e943dd6)는 엔진 soundness(재사용 시 매치 누락)만 고쳤고, 이 UX 비단조성은 기능 명세 자체에 내재한다. 쿼리가 자라면서 꺼질 수 있는 hard filter는 정의상 단조일 수 없어 어떤 가드로도 제거 불가능하다.
결정
chosung 옵션을 완전 제거한다. 초성 노이즈 억제가 다시 필요해지면 단조성-안전한 score demotion(초성 매치 감점 축)으로 재도입한다 — 이 이슈의 non-goal.
부수 효과: #35 가드 전체(prevTokenChosung, hasChosungFalseField, un-gating 검사)가 불필요해져 세션 재사용 로직이 단순해진다. 후속 scan cursor 이슈의 전제이기도 하다.
변경 사항
src/types.ts
MatchField.chosung 제거 (~L174)
SearcherFieldSpec.chosung 제거 (~L325)
src/matchFields.ts
isChosungOnlyToken 함수 제거 (이 변경 후 사용처 0 — export 포함 삭제). isConsonantLUT import도 미사용이 되므로 제거
- 토큰 루프의
const chosung = isChosungOnlyToken(token) (~L62) 및 if (chosung && fields[i].chosung === false) continue; (D4 hard filter, ~L67) 제거
- JSDoc의 chosung 언급 제거 (
@param fields, ~L31)
src/createSearcher.ts
isChosungOnlyToken import 제거
makeRuntime: hasChosungFalseField 파라미터, prevTokenChosung/currentTokenChosung 상태, canReuse 내 un-gating 분기 (~L177-179), 커밋 (~L227), resetSession 내 초기화, 관련 주석 (~L115-118, L153, L165-167) 제거
createMultiFieldSearcher: fieldBuf의 chosung: f.chosung (~L364), hasChosungFalseField 계산 (~L398)과 makeRuntime 5번째 인자 (~L405) 제거
- 제거 경고: 필드 spec에
chosung 키가 있으면 dev 모드(isProd가 false)에서 생성 시점에 console.warn — "[fuzzly] createSearcher: field option 'chosung' was removed and is ignored". JS 소비자의 silent ignore 방지. matchFields 직접 호출 경로에는 추가하지 않는다 (searcher에선 per-entry hot path라 비용/스팸 문제)
테스트
test/matchFields.test.ts case 6 ("chosung 스코프") 재작성: gate가 없으므로 초성-only 토큰이 모든 필드에서 후보가 된다. ㅎㄱㄷ가 chosung 옵션 없이 매치되고 weighted argmax로 귀속되는지 검증. 기존 r2 시나리오(양쪽 chosung:false → null)는 이제 non-null 매치가 되는 것으로 대체
test/multiField.test.ts:
- 마무리로
grep -ri chosung src test 0건 확인
문서
Breaking change
public 옵션 제거 (MatchField.chosung, SearcherFieldSpec.chosung). TS 소비자는 컴파일 에러로, JS 소비자는 dev 경고로 인지. 릴리스 노트에 명시할 것.
완료 기준
배경
chosung: false필드 옵션(멀티필드)은 초성-only 토큰을 해당 필드에서 hard filter로 gate-out한다 (matchFields.ts의 D4). 이 gate의 술어(isChosungOnlyToken)는 쿼리에 의존하므로, 타이핑 도중 모음이 붙는 순간(ㅍ→파) 술어가 뒤집혀 필드가 un-gate되고 매치 집합이 커진다.이는 라이브러리의 핵심 계약인 "타이핑할수록 결과 단조감소(monotonic narrowing)"를 UX 층위에서 깨뜨린다:
ㅍ→ gate-out → 결과 없음파→ un-gate → 결과 등장 (집합 증가)chosung:false필드에만 매치되는 아이템이 사라졌다 되살아나는 깜빡임 발생issue #35의 세션 가드(e943dd6)는 엔진 soundness(재사용 시 매치 누락)만 고쳤고, 이 UX 비단조성은 기능 명세 자체에 내재한다. 쿼리가 자라면서 꺼질 수 있는 hard filter는 정의상 단조일 수 없어 어떤 가드로도 제거 불가능하다.
결정
chosung옵션을 완전 제거한다. 초성 노이즈 억제가 다시 필요해지면 단조성-안전한 score demotion(초성 매치 감점 축)으로 재도입한다 — 이 이슈의 non-goal.부수 효과: #35 가드 전체(
prevTokenChosung,hasChosungFalseField, un-gating 검사)가 불필요해져 세션 재사용 로직이 단순해진다. 후속 scan cursor 이슈의 전제이기도 하다.변경 사항
src/types.tsMatchField.chosung제거 (~L174)SearcherFieldSpec.chosung제거 (~L325)src/matchFields.tsisChosungOnlyToken함수 제거 (이 변경 후 사용처 0 — export 포함 삭제).isConsonantLUTimport도 미사용이 되므로 제거const chosung = isChosungOnlyToken(token)(~L62) 및if (chosung && fields[i].chosung === false) continue;(D4 hard filter, ~L67) 제거@param fields, ~L31)src/createSearcher.tsisChosungOnlyTokenimport 제거makeRuntime:hasChosungFalseField파라미터,prevTokenChosung/currentTokenChosung상태, canReuse 내 un-gating 분기 (~L177-179), 커밋 (~L227),resetSession내 초기화, 관련 주석 (~L115-118, L153, L165-167) 제거createMultiFieldSearcher:fieldBuf의chosung: f.chosung(~L364),hasChosungFalseField계산 (~L398)과makeRuntime5번째 인자 (~L405) 제거chosung키가 있으면 dev 모드(isProd가 false)에서 생성 시점에console.warn—"[fuzzly] createSearcher: field option 'chosung' was removed and is ignored". JS 소비자의 silent ignore 방지.matchFields직접 호출 경로에는 추가하지 않는다 (searcher에선 per-entry hot path라 비용/스팸 문제)테스트
test/matchFields.test.tscase 6 ("chosung 스코프") 재작성: gate가 없으므로 초성-only 토큰이 모든 필드에서 후보가 된다.ㅎㄱㄷ가 chosung 옵션 없이 매치되고 weighted argmax로 귀속되는지 검증. 기존 r2 시나리오(양쪽chosung:false→ null)는 이제 non-null 매치가 되는 것으로 대체test/multiField.test.ts:chosung: true제거["ㅍ", "파", "판", "판결"]시퀀스에서 각 단계 결과 id 집합이 직전 단계의 부분집합인지(모든 단계에서 단조 축소), 최종 결과가 fresh searcher의search("판결")과 동일한지 assert. 세션 재사용 경로의 단조성 회귀를 계속 방어grep -ri chosung src test0건 확인문서
CLAUDE.md: "chosung un-gating 가드 (issue createSearcher 세션 최적화가 chosung:false 멀티필드에서 unsound (순방향 타이핑 시 항목 실종) #35)" 문단 삭제, 멀티필드 표의 "토큰 단위 chosung" 행 삭제, 세션 최적화 문단에서 관련 문장 정리Breaking change
public 옵션 제거 (
MatchField.chosung,SearcherFieldSpec.chosung). TS 소비자는 컴파일 에러로, JS 소비자는 dev 경고로 인지. 릴리스 노트에 명시할 것.완료 기준
grep -ri chosung src/ test/0건npm testgreen (재작성 테스트 포함)npm run check:fixclean