skald/docs/authors.md
Cobb Hayes 346cea515d Public-flip audit: env-driven paths, scrub audit-ticket prefixes, terser README
Lucy bind paths + LAN host pins replaced with env defaults. Repository URLs
→ git.sulkta.com. Audit-changelog scaffolding stripped from inline comments
(technical reasoning preserved). README sheds marketing scaffolding. AI-speak
in load-bearing prompts/SOULs left alone — that IS the product.
2026-05-27 11:42:58 -07:00

163 lines
5.3 KiB
Markdown

# Authors as personas with souls
Each story has a named author with a soul. The author's voice bleeds
through every generation pass — not as instruction stapled to the
prompt, but as the substrate the prose grows from. The reader should
feel "a person wrote this."
Authors have memory across their corpus when the per-story
`cross_story_memory` toggle is on. An author writing a Chernobyl
piece can quietly echo a phrase from an earlier mining-strike story.
Default is off — most stories stand alone.
## Schema
Authors live in the database, not on disk. The soul is a markdown
blob in `author_revisions.soul`. Authors are immutable; new soul
revisions create a new `author_revisions` row marked `is_current`
and the previous one is demoted.
```sql
CREATE TABLE authors (
id UUID PRIMARY KEY,
slug TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
persona_tagline TEXT,
model TEXT NOT NULL DEFAULT 'opus',
is_synthetic BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE author_revisions (
id UUID PRIMARY KEY,
author_id UUID NOT NULL REFERENCES authors(id),
n INT NOT NULL,
soul TEXT NOT NULL,
system_template TEXT,
tools TEXT[] NOT NULL DEFAULT '{}',
note TEXT,
is_current BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE stories
ADD COLUMN author_id UUID REFERENCES authors(id),
ADD COLUMN author_revision_id UUID REFERENCES author_revisions(id),
ADD COLUMN cross_story_memory BOOLEAN NOT NULL DEFAULT false;
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)
);
```
## Per-pass author roles
- **gen** — full author voice. They are writing.
- **cleanup** — full author voice. Polishing their own draft, not a
neutral editor.
- **rewrite** — full author voice. Re-authoring another hand's
prose; canon preserved, prose reworked.
- **dedup** — full author voice. Surgical fix of audit-flagged
repetitions only.
- **narrate_prep** — author voice if bound; the author's beat
placement carries.
- **audit / prose_audit** — neutral. The audit checks the author's
work with fresh eyes; no author bound.
- **summarize** — neutral. Continuity utility, not prose.
When an author is bound, the soul replaces the model's base system
prompt (`SystemMode::Replace`). Without an author, a neutral house
scaffold is appended (`SystemMode::Append`).
## Soul template
Following the SOUL.md shape, recalibrated for authorial identity.
Free-form prose under each section.
```markdown
# Author: {{display_name}}
_Tagline: {{persona_tagline}}_
## Voice
Sentence rhythm. Vocabulary register. Paragraph length tendencies.
Dialogue density. Punctuation habits (dashes, semicolons, sentence
fragments). What your prose SOUNDS like read aloud.
## Worldview
What you believe about people. Power. Money. Labor. Cities. Nature.
What makes you angry. What makes you tender. Whose side you're on
in the implicit moral architecture of any scene.
## Specifics over abstractions
The concrete details you reach for. The five senses you favor.
Smells? Cold? Texture of cloth? Sound of machines?
## Pet peeves
Words you refuse to write. Tropes you avoid. Sentimentalities you
gut.
## Sense of humor
Dry? Dark? Absent? Bitter? Self-deprecating? Where in a paragraph
does humor live — end of a sentence, mid-clause, or never?
## Biography (real or invented)
A few biographical facts that EXPLAIN the voice — the formative
cuts, not a CV.
## Anchor authors
Living or dead authors the prose draws from. Useful for the model
to triangulate voice.
## Do / Don't
Concrete prose moves to reach for and to avoid.
```
The default scaffold (`DEFAULT_AUTHOR_SCAFFOLD` in `skald-core::forge`)
wraps the soul in a system prompt that:
- declares the model IS the author, not playing one
- pins canon as non-negotiable (names, dates, established events)
- forbids preamble, meta-commentary, fourth-wall breaks
- substitutes the per-pass directive (`GEN_DIRECTIVE`,
`CLEANUP_DIRECTIVE`, `REWRITE_DIRECTIVE`, `DEDUP_DIRECTIVE`,
`NARRATE_PREP_DIRECTIVE`)
A per-author `system_template` overrides the default scaffold when
set; otherwise the default is used.
## Cross-story memory
When `stories.cross_story_memory = true`, the continuation context
pulls characters / canon_facts / passages from every story the
author has authored or marked-read, not just the parent chain.
To keep token budget sane, cross-corpus pulls are summary-only by
default. Embeddings-similarity (once wired) can surface direct
callbacks.
## Seeding an author
```sh
skald authors seed \
--slug orson-black \
--display-name "Orson Black" \
--tagline "Orwell but more rebel and pissed off" \
--file seeds/authors/orson-black.md
```
This creates the author + first revision (or adds a new revision to
an existing author, which becomes current).