Skip to content

feat(deep-audit-v1.21): CSP hardening + zh locale + coverage batches + VAD→Whisper WASM bridge#102

Merged
qnbs merged 16 commits into
mainfrom
feat/deep-audit-v1.21
Jun 9, 2026
Merged

feat(deep-audit-v1.21): CSP hardening + zh locale + coverage batches + VAD→Whisper WASM bridge#102
qnbs merged 16 commits into
mainfrom
feat/deep-audit-v1.21

Conversation

@qnbs

@qnbs qnbs commented Jun 9, 2026

Copy link
Copy Markdown
Owner

User description

Summary

  • fix(security): Harden web CSP — remove wss:* wildcard, add explicit AI provider endpoints (openrouter.ai, api.groq.com, api.mistral.ai, Anthropic) and streaming event source
  • feat(i18n): zh Beta locale translated to ≤5% EN placeholders (was ~71.5%)
  • test(coverage): Batch A+B+C — +22 tests for ecoModeService, deviceProfiler, voiceDownload, idbAssetStore, cloudSyncBackend codex/rag branches, normalizePersistedSettings defaults
  • feat(voice/B-2): VoiceActivityCoordinator — bridges WebRtcVadEngine PCM frames into WasmSttEngine via frame pump with MIN_SPEECH_CHUNKS gate and MAX_BUFFER_MS flush. Only activated when enableVoiceWasm + sttEngine.id === 'whisper'. voiceCommandService now routes start/stop/dispose through the coordinator on the WASM path. 12 unit tests.
  • fix: Test shape corrections — cloudSyncBackend codex (entities[]), idbAssetStore (originalFileName/byteSize), voiceDownloadAndIntent dispatch shim types + createLogger mock

Test plan

  • lint ✅ (0 errors, 0 warnings)
  • typecheck ✅ (tsgo --noEmit)
  • i18n:check ✅ (2348 keys × 11 locales)
  • voiceActivityCoordinator.test.ts (12/12)
  • voiceDownloadAndIntent.test.ts (10/10)
  • cloudSyncBackend.test.ts (28/28)
  • idbAssetStore.test.ts (13/13)
  • Full coverage + E2E → CI

🤖 Generated with Claude Code


CodeAnt-AI Description

Turn on core features by default, route Whisper voice input through speech detection, and tighten browser access

What Changed

  • New installs now start with most advanced features enabled, so users see the full product experience without turning options on one by one
  • Voice input with local Whisper now waits for speech, stops on silence, and restarts for the next utterance instead of running continuously
  • Browser access is narrowed to the AI providers and local voice services the app actually uses, removing broad WebSocket access
  • Chinese beta wording is filled out much more completely across the writing and world-building screens
  • Added coverage for eco mode, device profiling, voice downloads, local storage, and the new voice speech bridge

Impact

✅ Faster first-run setup
✅ Fewer stuck voice sessions
✅ Safer browser connections

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

qnbs and others added 6 commits June 9, 2026 02:05
…der endpoints

Removed `wss://*.workers.dev wss: ws:` open wildcards from index.html
connect-src (XSS WebSocket exfiltration vector). Added all AI provider
endpoints that were present in tauri.conf.json but missing from the web
CSP: xAI, Groq, OpenRouter, Ollama (localhost:11434), LM Studio
(localhost:1234), generic local AI (localhost:8000).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Translated all untranslated zh keys (worlds.json 52, writer.json 80,
settings.json 13, dashboard.json 1, common.json 1, export.json 1,
lora.json 1). Fixed 3 {{token}} localization issues (dashboard.momentum
.ofGoal, writer.studio.tools.improve.instruction, writer.chapter.label).
Result: 10/2358 EN-identical keys (0.4% placeholders, down from 6.3%).
ja was already at 0.9% — no changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ownload, idbAsset

- ecoModeService.test.ts: 17 tests covering battery threshold, isCriticalBattery,
  explicit override, listener lifecycle, applyAdaptiveMode with getBattery mock
- localAiDeviceProfiler.test.ts: 11 tests covering recommendBackend branches,
  cpuCores fallback, platform detection (pwa-web/tauri-desktop), cache TTL,
  invalidateDeviceProfile, profile shape
- voiceDownloadAndIntent.test.ts: 10 tests covering downloadVoiceModels
  (happy path, pre-aborted signal, mid-download abort, error, TTS model id),
  handleDictationResult (blank/non-blank), processTranscript (intent found/not found)
- idbAssetStore.test.ts: 13 tests covering image CRUD, binder asset CRUD,
  listBinderAssetIds prefix filter with cursor iteration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lizePersistedSettings defaults

- cloudSyncBackend.test.ts: +11 tests covering saveStoryCodex/getStoryCodex/deleteStoryCodex,
  saveRagVectors/getRagVectors (null→empty array)/deleteRagVectors, deviceId meta shape
- idbProjectStore.test.ts: +7 tests covering backup/voice/performance/notifications
  defaults, advancedAi defaults, keyboardShortcuts/writingGoals array normalization

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…T bridge

Wires WebRtcVadEngine PCM frames into WasmSttEngine via a dedicated
coordinator, completing the P1-2 VAD→Inference→Transcript pipeline.
Only activated when enableVoiceWasm + sttEngine.id === 'whisper'.

- New: services/voice/voiceActivityCoordinator.ts — frame pump, MIN_SPEECH_CHUNKS gate, MAX_BUFFER_MS flush, clean teardown
- feat: voiceCommandService.ts routes start/stop/dispose through coordinator when WASM path is active
- test: 12 coordinator unit tests (start/stop/dispose + error paths)
- fix: cloudSyncBackend test codex shape (entities[] not characters[])
- fix: idbAssetStore test renamed fields (originalFileName/byteSize)
- fix: voiceDownloadAndIntent dispatch shim types + createLogger mock
- chore: gitignore .translation-progress-*.json checkpoint files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- P1-2 marked as VAD→STT bridge complete (VoiceActivityCoordinator shipped)
- AUDIT.md follow-up chain extended with feat/deep-audit-v1.21 session entries
- Version bumped to v1.21.1 in AUDIT header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
storycraft-studio Ready Ready Preview, Comment Jun 9, 2026 8:33am

@codeant-ai codeant-ai Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Jun 9, 2026
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread index.html Outdated
Comment thread tests/unit/services/voice/voiceActivityCoordinator.test.ts Outdated
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

qnbs and others added 3 commits June 9, 2026 05:29
voiceCommandService now imports voiceActivityCoordinator which calls
createLogger at module-level. Any test that mocks services/logger and
transitively loads the coordinator needs createLogger in the mock factory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…mpotent init, CSP https:

- voiceActivityCoordinator: null segment from WebRtcVadEngine now preserves lastVadSpeechState
  instead of coercing to silence (was preventing MIN_SPEECH_CHUNKS from being reached during
  steady-state speech)
- flushStt: isFlushing guard prevents overlapping stop()+start() cycles over Whisper WASM's
  shared mediaStream/recorder — race would corrupt the capture session on every long utterance
- start(): isEngineInitialized flag prevents re-loading the 40 MB Whisper pipeline on every
  start() call; dispose() resets it so the next lifecycle reinitializes cleanly
- index.html: add https: to connect-src so user-configured openAiCompatibleBaseUrl endpoints
  (arbitrary remote proxies) are not blocked by CSP
- coordinator test: switch to vi.stubGlobal/vi.unstubAllGlobals pattern to prevent AudioContext
  global pollution across test files; FakeAudioContext defined at module level to close over
  mutable mockAudioCtxInstance; add behavior tests for all three fixed invariants
- .gitignore: suppress .translation-progress-*.json checkpoint files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pia writing theme

- featureFlagsSlice: all 20 flags now default to true so new installs get the full
  feature set immediately; enableRtlLayout stays false — ar/he locales are stubs only
  and enabling RTL layout without localized content breaks the UI
- settingsSlice: appearancePreset default changed from 'default' to 'sepia' — warm
  sepia tones reduce eye strain for long writing sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:XXL This PR changes 1000+ lines, ignoring generated files and removed size:XXL This PR changes 1000+ lines, ignoring generated files labels Jun 9, 2026
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread tests/unit/services/storage/idbAssetStore.test.ts
Comment thread tests/unit/services/storage/idbAssetStore.test.ts
Comment thread services/voice/voiceCommandService.ts
Comment thread services/voice/voiceActivityCoordinator.ts Outdated
Comment thread services/voice/voiceActivityCoordinator.ts
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

CodeAnt AI finished reviewing your PR.

qnbs and others added 6 commits June 9, 2026 07:47
…, mobile nav

Three root causes fixed:

Select dropdown (settings z-index bug):
- Select dropdown list: z-[--sc-z-docked] (10) → z-[--sc-z-sticky] (100)
- Dropdowns now appear above the VoiceControlPanel FAB (z-50) and mobile nav (z-40)
  so opening a select in Voice Settings no longer shows the mic button bleeding through

VoiceControlPanel (mobile layout):
- bottom-4 → bottom-20 on mobile, md:bottom-4 on desktop
- Clears the ~60px mobile bottom-nav bar (z-40); FAB was partially hidden behind it
- Desktop position unchanged (bottom-4 right-4)

ManuscriptEditor word count (overlap fix):
- Moved from absolute bottom-4 right-6 (collides with VoiceControlPanel FAB)
  to absolute bottom-20 left-6 md:bottom-4
- Mobile: word count and voice FAB are now side by side (both at bottom-20, left vs right)
- Desktop: word count at bottom-left, voice FAB at bottom-right — no overlap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ections

- PassphraseModal: add 'unlock' mode (re-derives in-memory key without overwriting sentinel), Spinner for busy state, focus-on-mount via useEffect+ref
- PrivacySection: fix critical bug — unlock button now opens 'unlock' modal instead of 'set'; add euDataResidency toggle (was in state but never rendered)
- SettingsShared ToggleSwitch: add hint prop with aria-describedby wiring
- IntegrationsSection: add ariaLabel to sync provider Select; role=status+aria-live on ltMsg feedback; comingSoon hint on unimplemented toggles (Evernote/Notion/Scrivener/GoogleDocs)
- CommunitySection: add focus-visible:ring-2 to community links; fix --sc-interactive-bg/fg (non-existent tokens) → --sc-info-bg/fg
- i18n: add euDataResidency + comingSoon keys to all 12 locales; rebuild bundles (2351 keys)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
voiceActivityCoordinator:
- MIN_SPEECH_CHUNKS 2 → 1: WebRtcVadEngine emits exactly one isSpeech edge per transition, so 2 was unreachable
- start(): add early-return guard if already running to prevent double mic stream leak

voiceCommandService:
- dispose() + null previous coordinator before creating new one on startListening() — prevents leaking audio capture sessions on repeated calls

idbAssetStore.test.ts:
- Fix storageBackend mock to match real production key contract (sanitize + '::' delimiter)
- Update all test key references from 'binder/proj/asset' to 'proj::asset'
- Fix getBinderAsset fixture: 'filename' → 'originalFileName', add byteSize: 4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…reset, 6 privacy toggles

- featureFlagsSlice.test: initialState all true except enableRtlLayout; defaultOn cases updated
- settingsSlice.test: appearancePreset 'default' → 'sepia'
- PrivacySection.test: 5 toggles → 6 (euDataResidency added); mock privacy includes euDataResidency
- plotBoardAiThunks.test: duckDbEnabled false → true (enableDuckDbAnalytics now defaults true)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ranslations

Lighthouse accessibility 0.92 → pass:
- index.css: --sc-text-muted darkened from #8a7f72 (3.33:1) to #655c50 (5.57:1 on
  #f4ecd8) — covers subtitle, FeatureHighlight descriptions, LanguageSelector chevrons
- index.css: added missing sepia state-color overrides (danger/success/warning/info fg+bg)
  that were inheriting dark-theme values (e.g. #4ade80 success-fg = 1.48:1 on sepia bg)

Help center audit & translations:
- locales/en/help.json: 9 keys rewritten (desktop stub, privacy FAQ, API key FAQ,
  export formats, flags list 12→21, feature flag count, SHA-256 iter 310k→600k)
- locales/{de,fr,es,it,ja,zh,pt,el,ar,he}/help.json: same 9 keys force-translated via
  translate-help-forced.mjs (POST to translate.googleapis.com, no key required)
- scripts/translate-help-forced.mjs: new script for force-updating specific stale keys
  across all 10 non-EN locales with proper HTML POST (avoids GET URL-length limits)
- public/locales/*/bundle.json: rebuilt — 2351 keys × 11 locales

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
E2E axe caught two color-contrast failures in the Settings view:
  1. <span>General</span> in active settings nav: --nav-text-active #b45309
     on blended active-bg (#ecdabf) = 3.66:1 (needs 4.5:1)
  2. <button class="text-[var(--sc-accent)]">Sample link</button> in
     AccessibilitySection: #b45309 on #f4ecd8 = 4.26:1 (needs 4.5:1)

Root cause: --sc-accent #b45309 (was also --nav-text-active) is too light
for text use on sepia's warm beige surfaces.

Fix: darken accent family in .appearance-sepia
  - --sc-accent:           #b45309 → #92400e  (6.02:1 on base, 5.05:1 on active-nav-bg)
  - --sc-accent-hover:     #92400e → #78350f
  - --sc-accent-subtle:    rgba(180,83,9,0.14) → rgba(146,64,14,0.14)
  - --sc-accent-glow:      rgba(180,83,9,0.28) → rgba(146,64,14,0.28)
  - --sc-border-focus:     #b45309 → #92400e
  - --sc-ring-focus:       rgba(180,83,9,0.45) → rgba(146,64,14,0.45)
  - --nav-text-active:     #b45309 → #92400e
  - --nav-background-active: rgba(180,83,9,0.12) → rgba(146,64,14,0.12)
  - --sc-warning-fg:       #92400e → #9a3412 (now distinct from accent)
White text on primary buttons: 7.09:1 ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The E2E axe color-contrast failures in the sepia theme (fgColor #6d6457 on
bgColor #e4d9c5, ratio 4.16:1) were caused by the command-palette fade-in
animation running at partial opacity (~0.94) when axe sampled the element.
Playwright emulates prefers-reduced-motion:reduce but the animate-in/fade-in
class was not gated behind motion-reduce. axe blends fg+bg at that opacity and
reports an effective fg that is lighter than the CSS value, producing a false
contrast failure.

Adding motion-reduce:animate-none to the dialog panel ensures it renders at
full opacity instantly under reduced-motion, so axe sees #655c50 on #e8dcc8
(5.19:1, well above 4.5:1 AA). Also adds motion-reduce:transition-none to the
backdrop overlay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@qnbs
qnbs merged commit 364025e into main Jun 9, 2026
16 checks passed
@qnbs
qnbs deleted the feat/deep-audit-v1.21 branch June 9, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant