feat(tag-vocab): telemetryEvents — tag_vocab_hit/miss zod schemas (#3 v0.2.3)
- TagVocabHitPayload { tagId: int>0, vocabSize: int>=0 } .strict()
- TagVocabMissPayload { vocabSize: int>=0 } .strict()
- TelemetryEventSchema union 13 → 15
- 단위 +3 cases (hit accept, miss accept, hit extra field 거부)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,15 @@ const AiRetryManualPayload = z.object({
|
||||
failedCount: z.number().int().positive()
|
||||
}).strict();
|
||||
|
||||
const TagVocabHitPayload = z.object({
|
||||
tagId: z.number().int().positive(),
|
||||
vocabSize: z.number().int().nonnegative()
|
||||
}).strict();
|
||||
|
||||
const TagVocabMissPayload = z.object({
|
||||
vocabSize: z.number().int().nonnegative()
|
||||
}).strict();
|
||||
|
||||
export const TelemetryEventSchema = z.discriminatedUnion('kind', [
|
||||
z.object({ ts: z.string(), kind: z.literal('capture'), payload: CapturePayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('ai_succeeded'), payload: AiSucceededPayload }).strict(),
|
||||
@@ -63,7 +72,9 @@ export const TelemetryEventSchema = z.discriminatedUnion('kind', [
|
||||
z.object({ ts: z.string(), kind: z.literal('ollama_unreachable'), payload: OllamaUnreachablePayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('ollama_recovered'), payload: OllamaRecoveredPayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('ollama_recheck_manual'), payload: EmptyPayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('ai_retry_manual'), payload: AiRetryManualPayload }).strict()
|
||||
z.object({ ts: z.string(), kind: z.literal('ai_retry_manual'), payload: AiRetryManualPayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('tag_vocab_hit'), payload: TagVocabHitPayload }).strict(),
|
||||
z.object({ ts: z.string(), kind: z.literal('tag_vocab_miss'), payload: TagVocabMissPayload }).strict()
|
||||
]);
|
||||
|
||||
export type TelemetryEvent = z.infer<typeof TelemetryEventSchema>;
|
||||
|
||||
@@ -278,3 +278,31 @@ describe('ai_retry_manual event', () => {
|
||||
})).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateEvent — tag vocab', () => {
|
||||
it('accepts tag_vocab_hit event', () => {
|
||||
const e = validateEvent({
|
||||
ts: '2026-05-02T00:00:00.000Z',
|
||||
kind: 'tag_vocab_hit',
|
||||
payload: { tagId: 42, vocabSize: 17 }
|
||||
});
|
||||
expect(e.kind).toBe('tag_vocab_hit');
|
||||
});
|
||||
|
||||
it('accepts tag_vocab_miss event without tagId', () => {
|
||||
const e = validateEvent({
|
||||
ts: '2026-05-02T00:00:00.000Z',
|
||||
kind: 'tag_vocab_miss',
|
||||
payload: { vocabSize: 17 }
|
||||
});
|
||||
expect(e.kind).toBe('tag_vocab_miss');
|
||||
});
|
||||
|
||||
it('rejects tag_vocab_hit with extra field (privacy invariant)', () => {
|
||||
expect(() => validateEvent({
|
||||
ts: '2026-05-02T00:00:00.000Z',
|
||||
kind: 'tag_vocab_hit',
|
||||
payload: { tagId: 42, vocabSize: 17, tagName: 'leak' }
|
||||
})).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user