Long-form story-writer with canon-keeping, sequel-continuity, and self-hosted narration. Database-is-source-of-truth — writer is the tooling.
skald narrate pre-processes body_md_tts with word-boundary regex substitutions from pronunciation_overrides where phoneme_format = 'respelling'. Story-scoped overrides win over global; longer words substitute first (so 'Bryukhanov' wins over a hypothetical 'Bry' override). Case is preserved on the first letter so 'Pripyat' at sentence start stays capitalised after respelling to 'Prip-yat'. Seeded Coast-Down with 52 entries: Russian/Ukrainian surnames (Dyatlov, Akimov, Bryukhanov, Stolyarchuk, Yuvchenko, Khmel, etc.), first names (Pyotr, Lyudmilla, Anatoly, Vasily, Sasha, Aleksandr, Leonid), patronymics (Stepanovich, Fyodorovich, Mykolaivna, Hryhorivna), and places (Pripyat, Chernobyl, Kyiv, Kopachi, Lubyanka). Plus the operational acronyms NIKIET, RBMK, AZ-5, SIUR, SIUB, ChNPP, MSCh. Other phoneme_format values (ipa, arpabet) are no-ops for now — Kokoro's misaki tokenizer doesn't expose a stable lexicon-injection API across the HTTP boundary in v0.1. Future: pass IPA forms in a new server-side request field and inject into the pipeline's g2p lexicon for more accurate phonetics. |
||
|---|---|---|
| docs | ||
| migrations | ||
| seeds/authors | ||
| skald | ||
| skald-core | ||
| vendor/clawdforge | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| compose.yml | ||
| Dockerfile | ||
| entrypoint.sh | ||
| README.md | ||
skald
Long-form story-writer with canon-keeping, sequel continuity, and (future) self-hosted audiobook narration. Database is the source of truth — the writer is the tooling.
Named for the Old Norse poets who composed and memorized kings' sagas across generations.
Status: v0.1 — scaffold
What's wired:
- Rust workspace (
skald-core+skald) - Postgres schema for stories, characters, canon facts, chapters, passages, generation runs, audit findings, tags
- pgvector extension installed for future similarity search
skald import-markdowningests a story file (chapters + bible) into the schemaskald serveexposes/healthand runs migrations on boot- Single-container deploy: postgres + skald in one image
Wired (this commit):
- clawdforge Rust SDK vendored at
vendor/clawdforge/(upstream:Sulkta-Coop/clawdforgeclients/rust/) skald-core::forge— three-pass orchestration shell (gen / cleanup / audit). Prompts are TODO stubs; pipeline plumbing is in place.
Not yet wired:
- Web UI (the inbox + browse + queue surface)
- Prompt templates for the three passes (heavy prompt-engineering work — own session)
skald-core::context— assemble the LLM context blob from DB rows (bible + characters + parent prose summaries + similarity-matched passages)- Embeddings backfill + ivfflat index
- TTS sidecar container + post-render audit chain (see
docs/tts-pipeline.md)
v0.1 smoke
docker compose -p skald up -d
docker exec skald skald import-markdown \
--path /seed/coast-down.md \
--title "The Coast-Down"
curl http://lucy:7780/health
# → { ok: true, db_ok: true, story_count: 1, ... }
Schema (cheat sheet)
stories → meta + status + parent/root for series
characters → real or fictional, story-scoped
canon_facts → setting, mystery, theme, rule, historical_anchor, hook
chapters → full prose body
chapter_summaries → short summaries for cheap context loading
passages → paragraph-level + embedding vector(1536)
generation_runs → every LLM call logged
audit_findings → canon audit output (severity + area)
tags → arbitrary labels
Architecture (v0.1 + the plan)
┌─────────────────────────────────┐
│ skald container │
│ ┌───────────┐ ┌────────────┐ │
│ │ postgres │ │ skald-rust │ │
│ │ pgvector │←─│ axum + cli │ │
│ │ localhost │ │ :7780 │ │
│ └───────────┘ └─────┬──────┘ │
└─────────────────────────┼────────┘
│ HTTP (future)
↓
┌──────────┐
│clawdforge│
└─────┬────┘
↓
opus calls
v1.0+: extract postgres to its own container on db-net. skald
becomes pure stateless rust, connects via DATABASE_URL. Migration
is a connection-string change + a network move; the binary doesn't
care where the DB lives.
License
MIT.