* feat(gallery): designed-voice archetype gallery + neutral importer
Adds a browsable library of ~1,100 designed voice archetypes (no real
people), generated from OmniVoice's own voice-design taxonomy and
organized ElevenLabs-style: 24 curated Featured voices plus a
facet-filtered "Browse all" explorer (595 English + 504 Chinese-dialect).
Every generated instruct is built from the validator's own vocabulary, so
none can trigger the issue-#89 synthesis crash.
Backend:
- core/archetypes.py: catalog engine (featured + generated, implausible
combos pruned, stable hashed ids); loads the taxonomy by file path to
stay torch-free in tests.
- api/routers/archetypes.py: categories / list+filter+paginate / get /
preview (render-on-demand + disk cache) / use (materialize a voice
profile). Preview/use reuse generation.py's proven inference path.
- gallery.py: drop the celebrity/character catalog; the importer is now a
neutral, user-driven "My Imports" (paste a URL you have the rights to).
No project-shipped directory of named real people.
Frontend:
- Gallery UI rewrite: Archetypes zone (featured grid + facet filters +
favorites) and My Imports zone; per-card Use voice / Open in Designer.
- api/archetypes.ts, useArchetypes/useArchetypeCategories hooks (v5
placeholderData:keepPreviousData), gallerySlice, en.json keys.
Tests: 27 new (engine contract + API), full backend suite green (72);
CJK guard allowlists the one functional Chinese preview sample.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(gallery): clear new bandit alerts (sha1 + SQL false-positive)
The PR's code-scanning "Bandit" check fires on NEW alerts vs main's baseline.
The archetype work introduced three:
- archetypes.py / core/archetypes.py: hashlib.sha1 used to derive a
deterministic preview-cache key and archetype id (not a security digest) —
flagged B324 (HIGH). Add usedforsecurity=False; the digest is unchanged.
- gallery.py: the UPDATE query interpolates only static, code-controlled column
fragments ("is_favorite = ?", "description = ?"); every user value is bound
via a ? placeholder — flagged B608 (false positive). Annotate `# nosec B608`
with the justification.
Behavior-preserving. Net new bandit alerts after this: zero (verified with
bandit -ll -ii; only main's pre-existing baseline remains).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(gallery): resolve PR #203 CI (SHA-256 ids, log sanitization, CJK allowlist)
All failures stemmed from the initial commit:
- Bandit + CodeQL (2 high): SHA-1 weak-hash on the archetype id and the
preview cache key. These are deterministic identifiers, never security
digests — switched to SHA-256, which the SAST scanners accept.
- CodeQL (log injection): the render-failure logs echoed the raw
user-supplied archetype_id; log the catalog's canonical a["id"] instead
(untainted — it comes from the trusted in-memory catalog, not the request).
- CodeQL (superfluous argument): declare createGallerySlice's StateCreator
store param so its arity matches the 3-arg call site.
- Tests (test_no_hardcoded_cjk): the committed design spec's Chinese-dialect
reference table tripped the guard; allowlist it under documentation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(gallery): clear CodeQL clear-text-logging on archetype render errors
CodeQL's sensitive-data heuristic flags any request-derived value
interpolated into a log call (it persisted even after switching the raw
id to the catalog's canonical a["id"]). Log a static message with
exc_info=True instead: the full traceback still reaches the backend log
for debugging, but no data expression remains for the query to flag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>