Phase 5: voice consistency pass

Tier-1 voice-consistency pass: whole-document LLM review surfacing passages
that read tonally out of place (formal/over-polished/paraphrased-too-closely),
as honey-decorated `voice` flags with no correction (awareness-only).

- internal/llm/voice.go: RunVoice sends the whole document (no TruncateDoc),
  MaxTokens 2048, 20s per-doc floor (VoiceInterval). Standalone voice prompt
  in prompts.go (not bundled with the grammar checkpoint, per spec).
- internal/suggestions: POST /api/docs/:id/voice. replacePending is now
  family-scoped (pendingScope) so grammar and voice never clobber each other's
  pending flags; both passes return the unified pending set. check/voice share
  one runPass helper. TestVoicePassCoexists covers both directions.
- Frontend: api.voiceDoc, useCheckpoint voicing/runVoice, honey "Check my
  voice" toolbar pill, breathing honey dot in StatusBar.

Claude-Session: https://claude.ai/code/session_016Yr6jELuRc7hyzYLccQKZd
This commit is contained in:
prosolis
2026-06-25 21:16:53 -07:00
parent 3c5f3ecb96
commit 0fa70979a0
12 changed files with 342 additions and 59 deletions

View File

@@ -70,7 +70,12 @@ export const api = {
// Grammar checkpoint: runs an LLM pass and returns the fresh pending set.
// Rate-limited per document server-side (returns the existing set if too soon).
// Both passes return the UNIFIED pending set (grammar + voice), so the client
// never drops one family's highlights when the other refreshes.
checkDoc: (id: string) => req<Suggestion[]>(`/docs/${id}/check`, { method: 'POST' }),
// Voice-consistency pass: whole-document, explicit-action, slower. Returns the
// unified pending set too. Rate-limited per document server-side.
voiceDoc: (id: string) => req<Suggestion[]>(`/docs/${id}/voice`, { method: 'POST' }),
// Pending suggestions for a doc, loaded when the editor opens it.
listSuggestions: (id: string) => req<Suggestion[]>(`/docs/${id}/suggestions`),
acceptSuggestion: (id: string) =>