* feat(gallery): save gallery voices as profiles, with validated audio references Work-in-progress lifted from the concurrent gallery session at the owner's request (its uncommitted working tree, preserved verbatim from base 92b1ee5d; safety snapshot remains at rescue/gallery-wip): - gallery voices can be saved as local profiles: audio is copied into the profile store with content-addressed filenames, existing profiles are detected and refreshed only when the source clip changed - backend/core/audio_validation.py: symlink-rejecting, root-contained resolution for persisted profile WAV references, with tests - archetype/community routers and the Voice Gallery UI updated for the save-as-profile handoff (spec: docs/specs/longform/26-gallery-use-handoff.md) - locale updates for the new gallery strings across all 21 files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore: drop a stray local screenshot script that rode in with the tree copy * fix(community): explain the tolerated Content-Length parse failure; drop an unused import CodeQL on #1542: the empty except now says why it is safe (the streamed byte counter enforces the same cap regardless), and the test file loses an unused Path import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(gallery): review findings — copy outside the write lock, no stale completions CodeRabbit on #1542, all findings addressed: - the profile-audio copy stages to a .part temp BEFORE BEGIN IMMEDIATE and publishes via atomic os.replace inside it — other backend writers no longer block for the duration of an audio copy; a mid-copy failure leaves no temp droppings and no profile row (both pinned by tests) - VoiceGallery async ops carry per-operation generation tokens: a preview or save-as-profile that resolves after unmount (or after a newer operation) can no longer play audio, redirect into a workspace, or touch state — three fail-before regression tests - VoiceGalleryActions imports the page at test runtime; the e2e locator uses a stable data-testid instead of a translated string; symlink tests skip cleanly where the OS can't create symlinks; the changelog line carries its PR ref Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: static ffmpeg fallback when the chocolatey feed is down Third feed outage to break a PR run (2026-07-20, 2026-07-28, today — three attempts, three 'installed 0/1'). Chocolatey is a distribution channel, not the dependency: after the retry loop exhausts, fetch the static gyan.dev build from its GitHub release mirror and put it on PATH — same binary, no feed in the path. URL verified live (HTTP 200). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Longform / roadmap implementation specs
Generated by the longform-roadmap-specs workflow (draft + iterative refinement). Each maps to a task (#21–#34).
| # | Spec | Title | size |
|---|---|---|---|
| 21 | gpu-compat-matrix | GPU compatibility matrix + preflight | 101k |
| 22 | shared-voice-selector | Shared component | 88k |
| 23 | transcriptions-import | Import from Transcriptions → script/lines | 92k |
| 24 | story-audiobook-export | Story <-> Audiobook export/convert | 86k |
| 25 | inline-create-voice | Inline Create Voice in cast/narrator | 94k |
| 26 | gallery-use-handoff | Gallery "Use in Stories"/default + create-voice handoff | 85k |
| 27 | unify-longform-parsers | Unify the 3 longform parsers | 92k |
| 28 | two-pass-acx-mastering | Two-pass ACX loudness mastering | 103k |
| 29 | ovsvoice-format | .ovsvoice portable persona format | 82k |
| 30 | dub-to-stories | Dub -> Stories cast+lines export | 81k |
| 31 | unified-longform-project-store | Unified LongformProject store | 101k |
| 32 | phone-calls | Opt-in phone calls (agentic) | 101k |
| 33 | cue-sheet-export | Standalone chapter cue-sheet (.txt) export | 69k |
| 34 | runtime-verify | Runtime-verify shipped longform features | 119k |