# skald Long-form story-writer with canon-keeping, sequel continuity, and self-hosted audiobook narration. The database is the source of truth; the binary is the tooling. Named for the Old Norse poets who composed and memorized kings' sagas across generations. ## What's wired - Rust workspace (`skald-core` + `skald`) - Postgres schema for stories, characters, canon facts, chapters, passages, generation runs, audit findings, tags - pgvector for future similarity search - `skald import-markdown` ingests a story file (chapters + bible) - `skald serve` exposes `/health` + the web inspector and runs migrations on boot - `skald continue` runs gen → cleanup → audit per chapter, with multi-chapter batching (cap 20) - `skald rewrite` re-authors a chapter in a named author's voice - `skald audit` runs whole-story prose-quality audit; `skald dedup` is the surgical fix half of the loop - `skald prepare-narration` annotates a chapter with `[breath]` / `[pause:Xs]` / `[scene]` beats and per-character `[voice:...]` tags - `skald narrate` renders a chapter to audio via one of three TTS engines (F5-TTS, Kokoro-82M, Tortoise-TTS) — see `engines/` - Named-author "soul" personas via `skald authors seed`; author voice replaces the model's base system prompt for gen/cleanup/ rewrite/dedup/narrate_prep ## Schema (cheat sheet) ``` stories meta + status + parent/root for series authors persona identity (slug, display_name, model) author_revisions versioned souls; one current per author characters real or fictional, story-scoped, voice-mappable canon_facts setting, mystery, theme, rule, historical_anchor, hook chapters full prose body + optional body_md_tts annotation chapter_summaries short summaries for cheap context loading passages paragraph-level + embedding vector(1536) voices TTS voice rows (F5 ref clips / Kokoro / Tortoise names) pronunciation_overrides per-story + global respellings for proper nouns generation_runs every LLM call logged audit_findings audit pass output (severity + area) narration_runs per-chapter TTS renders ``` ## Quickstart ```sh docker compose up -d docker exec skald skald import-markdown --path /seed/.md \ --title "" curl http://localhost:7780/health ``` The compose file expects `POSTGRES_PASSWORD` and (optionally) `CLAWDFORGE_URL` + `CLAWDFORGE_TOKEN` in `.env`. Story markdown goes into `./seed/`; postgres data persists in `./pgdata/`. ## Architecture v0.1 ships postgres inside the skald container — singleton until the tool stabilises. To extract postgres later, swap the runtime base to `debian:bookworm-slim`, drop `entrypoint.sh`, and point `DATABASE_URL` at the external pg. The binary doesn't care where the DB lives. The generation passes call out to `clawdforge` (a bearer-token-gated HTTP wrapper around `claude -p`). The Rust client is vendored at `vendor/clawdforge/`. TTS calls go HTTP+JSON to the per-engine sidecars under `engines/`. ## License MIT — see `LICENSE`.