From 26f1db562616dff0303b5096a1fd7abb7695d49d Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 2 May 2026 12:29:24 +0900 Subject: [PATCH] =?UTF-8?q?feat(tag-vocab):=20TelemetryService=20EmitInput?= =?UTF-8?q?=20+tag=5Fvocab=5Fhit/miss=20+=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?narrowing=20=ED=99=95=EC=9E=A5=20(#3=20v0.2.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - EmitInput union 13 → 15 - narrowing guards (noteId 없는 kind 분기) 에 tag_vocab_hit/miss 추가 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/main/services/TelemetryService.ts | 4 +++- tests/unit/TelemetryService.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/services/TelemetryService.ts b/src/main/services/TelemetryService.ts index d44d574..dc63121 100644 --- a/src/main/services/TelemetryService.ts +++ b/src/main/services/TelemetryService.ts @@ -28,7 +28,9 @@ export type EmitInput = | { kind: 'ollama_unreachable'; payload: { reason: string } } | { kind: 'ollama_recovered'; payload: { downtimeMs: number } } | { kind: 'ollama_recheck_manual'; payload: Record } - | { kind: 'ai_retry_manual'; payload: { failedCount: number } }; + | { kind: 'ai_retry_manual'; payload: { failedCount: number } } + | { kind: 'tag_vocab_hit'; payload: { tagId: number; vocabSize: number } } + | { kind: 'tag_vocab_miss'; payload: { vocabSize: number } }; export class TelemetryService { constructor( diff --git a/tests/unit/TelemetryService.test.ts b/tests/unit/TelemetryService.test.ts index aa700cd..0e41f86 100644 --- a/tests/unit/TelemetryService.test.ts +++ b/tests/unit/TelemetryService.test.ts @@ -148,7 +148,7 @@ describe('TelemetryService.readAllRecent', () => { expect(events).toHaveLength(3); // discriminant narrowing — noteId 없는 kind(empty_trash/expired_banner_shown/expired_batch_trash) 가 섞이면 명시적으로 실패 expect(events.map((e) => - (e.kind === 'empty_trash' || e.kind === 'expired_banner_shown' || e.kind === 'expired_batch_trash' || e.kind === 'ollama_unreachable' || e.kind === 'ollama_recovered' || e.kind === 'ollama_recheck_manual' || e.kind === 'ai_retry_manual') + (e.kind === 'empty_trash' || e.kind === 'expired_banner_shown' || e.kind === 'expired_batch_trash' || e.kind === 'ollama_unreachable' || e.kind === 'ollama_recovered' || e.kind === 'ollama_recheck_manual' || e.kind === 'ai_retry_manual' || e.kind === 'tag_vocab_hit' || e.kind === 'tag_vocab_miss') ? null : e.payload.noteId )).toEqual(['a', 'b', 'b']); @@ -164,7 +164,7 @@ describe('TelemetryService.readAllRecent', () => { expect(events).toHaveLength(1); const ev = events[0]!; expect(ev.kind).toBe('capture'); - if (ev.kind !== 'empty_trash' && ev.kind !== 'expired_banner_shown' && ev.kind !== 'expired_batch_trash' && ev.kind !== 'ollama_unreachable' && ev.kind !== 'ollama_recovered' && ev.kind !== 'ollama_recheck_manual' && ev.kind !== 'ai_retry_manual') expect(ev.payload.noteId).toBe('a'); + if (ev.kind !== 'empty_trash' && ev.kind !== 'expired_banner_shown' && ev.kind !== 'expired_batch_trash' && ev.kind !== 'ollama_unreachable' && ev.kind !== 'ollama_recovered' && ev.kind !== 'ollama_recheck_manual' && ev.kind !== 'ai_retry_manual' && ev.kind !== 'tag_vocab_hit' && ev.kind !== 'tag_vocab_miss') expect(ev.payload.noteId).toBe('a'); }); it('returns [] when dir missing', async () => {