-- Authors as personas with souls (v0.3). -- -- Each story has a named author whose soul (SOUL.md-style markdown -- blob) IS the LLM's system prompt. Soul replaces opus's default -- base prompt via clawdforge's system_mode='replace' so the model -- BECOMES the author, not "Claude playing the author." -- -- Souls are versioned: authors.id is the immutable identity, while -- author_revisions tracks soul edits over time. Stories pin to a -- specific revision so "this was the version of Orson Black active -- when chapter 8 of Coast-Down was written" is always recoverable. CREATE TABLE authors ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug TEXT NOT NULL UNIQUE, -- "orson-black" display_name TEXT NOT NULL, -- "Orson Black" persona_tagline TEXT, -- "Orwell but more rebel + pissed off" -- Model the author writes with. Skald is opinionated: always -- opus max effort. Stored per-author so we can later vary -- (e.g. test sonnet for a faster-iterating author). model TEXT NOT NULL DEFAULT 'opus', -- Flag for the operator: this is a synthetic literary persona, -- not a real human author. Display somewhere obvious. is_synthetic BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- One row per soul revision. `n` is monotonic per author. Exactly -- one revision per author is_current=true at any time (partial -- unique index). CREATE TABLE author_revisions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), author_id UUID NOT NULL REFERENCES authors(id) ON DELETE CASCADE, n INTEGER NOT NULL, -- The soul: SOUL.md-style markdown blob. Replaces claude's base -- system prompt when this revision is used. soul TEXT NOT NULL, -- Optional override of the default scaffold prompt that wraps -- the soul. Default scaffold lives in skald-core::forge. system_template TEXT, -- Tools the author can call during gen. Empty default — fiction -- writing doesn't normally need them. Researcher-bent authors -- could opt in for WebSearch / Read. tools TEXT[] NOT NULL DEFAULT '{}', -- Human note: "first cut", "tightened voice after Chapter 8 read" note TEXT, is_current BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE (author_id, n) ); -- At most one current revision per author. CREATE UNIQUE INDEX idx_author_revisions_current ON author_revisions(author_id) WHERE is_current = true; CREATE INDEX idx_author_revisions_author ON author_revisions(author_id); -- Auto-touch authors.updated_at on author or revision change. CREATE OR REPLACE FUNCTION touch_author_updated_at() RETURNS TRIGGER AS $$ BEGIN UPDATE authors SET updated_at = now() WHERE id = COALESCE(NEW.author_id, OLD.author_id); RETURN COALESCE(NEW, OLD); END; $$ LANGUAGE plpgsql; CREATE TRIGGER author_revisions_touch_author AFTER INSERT OR UPDATE OR DELETE ON author_revisions FOR EACH ROW EXECUTE FUNCTION touch_author_updated_at(); -- Stories carry author identity + the exact revision used at gen -- time. author_id is also denormalized on the row (instead of -- joining through revision) so common "all stories by Orson" -- queries don't need the join. ALTER TABLE stories ADD COLUMN author_id UUID REFERENCES authors(id) ON DELETE SET NULL, ADD COLUMN author_revision_id UUID REFERENCES author_revisions(id) ON DELETE SET NULL, -- Per-story toggle for cross-story memory. When true, -- ContinuationContext::assemble pulls characters / canon / -- summaries from EVERY story in the author's corpus, not just -- the parent chain. ADD COLUMN cross_story_memory BOOLEAN NOT NULL DEFAULT false; CREATE INDEX idx_stories_author ON stories(author_id); -- Track which stories an author has access to. Auto-populated via -- app code on every story creation (role='authored'); operator can -- mark other stories as 'read' for cross-corpus memory. CREATE TABLE author_corpus ( author_id UUID NOT NULL REFERENCES authors(id) ON DELETE CASCADE, story_id UUID NOT NULL REFERENCES stories(id) ON DELETE CASCADE, role TEXT NOT NULL CHECK (role IN ('authored', 'read')), added_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (author_id, story_id) ); CREATE INDEX idx_author_corpus_author ON author_corpus(author_id);