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.
163 lines
5.3 KiB
Markdown
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).
|