Skip to content

notebook/

What a persona keeps about one conversation, across its turns. Each file under notebook/ is one reusable set of typed slots; a persona includes sets by name.

A pastor writes an index card after a visit — their name, who they are worried about, what you left them with, which chapter you read. That card is not one kind of fact. Some of it is a reading, some is a note, some is a list of things to come back to, and some of it points at particular people and passages. Declaring only the readings would have been a metrics facility, and a persona would then keep its numbers in the host and its notes nowhere.

notebook/pastoral.yml
name: pastoral
description: What a pastor reads in a conversation, and how he is bearing it himself.
slots:
- name: rapport
scope: conversation
description: "How much trust there is. 1 wary, 10 they tell you what they tell nobody."
type: ordinal
range: [1, 10]
default: 4
- name: stage
scope: subject
description: "What has actually been SAID about faith, not what he hopes."
type: stage
stages: [unspoken, curious, asking, wrestling, seeking, declined]
default: unspoken
- { name: contentment, scope: agent, type: ordinal, range: [1, 10], default: 6 }
- { name: questionsOpen, scope: conversation, type: count, default: 0 }
- { name: askedToStop, scope: subject, type: boolean, default: false }
- { name: card, scope: subject, type: text, limit: 400, default: "" }
- { name: toRead, scope: conversation, type: list, limit: 5, default: [] }
- { name: about, scope: conversation, type: entities, limit: 8, default: [] }
Field Required Meaning
name yes The set’s id, referenced from a persona. Unique across the world.
description no One line on what the set is for.
slots[].name yes The slot’s id, unique within the set.
slots[].scope yes subject (the person spoken to), agent (itself), conversation (the exchange).
slots[].type yes One of the types below.
slots[].description no What it holds, in the words the extraction step reads.
slots[].default yes The value before anything has been observed.
slots[].range for ordinal/ratio [min, max] inclusive. A host clamps to it rather than failing a turn.
slots[].stages for stage The ordered states. Movement need not be forward.
slots[].limit no Longest text; most items in a list or entities. A host caps whether or not one is given.
Type Holds For
ordinal a whole number on a declared scale a reading: rapport, mood, confidence
stage one of a declared, ordered set of states where a decision has got to
count a non-negative whole number questions still owed
ratio a real number on a declared scale a proportion
boolean true or false a fact that has or has not happened
text a short note in prose what they said, what you left them with
list bounded short strings links, references, things to come back to
entities {label, id, name?} references WHICH people, passages or accounts this was about

type is load-bearing. A funnel is not a one-to-ten score, an objection count is neither, and a note is none of them: a facility that expressed everything as one scale would misreport most of the card. A host validates a value against its slot’s own declaration.

entities is not a list of strings. An entity reference points back into the graph, so an id with its label can be resolved again and a conversation can carry what it was about rather than a prose approximation. A reference missing either half is dropped rather than repaired: an id with no label cannot be looked up, a label with no id names a type rather than a thing, and inventing the missing half puts a reference in the notebook that resolves to nothing — or to something else.

Two slot names are reserved behaviour. askedToStop and inDistress are brakes: once true they stay true for the conversation, and while either is set a host withholds the persona’s objective. An advocate asked to stop is not told to stop advocating — it is simply no longer told what it was trying to achieve, because stating an objective and a prohibition together is contradictory input, and a model resolves that by acknowledging the request and then proceeding anyway.

The host, once per turn, before the reply is written: a second model call reads what the person has just said and returns the new contents, validated against the declarations above. That call is named by LLM role, and the role must be one of the ids in that table — routing is the one meant for it. An unknown role resolves to the host’s default rather than refusing, so an invented id does not fail; it silently runs whatever the default is and the author never learns which.

# in a persona's brief.yml
notebook:
sets: [pastoral]
extraction:
role: routing
every: turn # `turn`, or `close` to extract once when the conversation ends

Extraction runs before generation and not after, which is not an ordering preference. Extracting afterwards leaves every slot one turn stale, and for a brake that is a defect rather than a lag: a persona asked to stop said “no pressure” and volunteered a reading in the same breath, because the prompt that wrote it still believed nobody had objected.

A realm declares notebook sets; the world grants them, and may grant some and refuse others. A persona whose sets are all refused still runs — it simply keeps no notebook. The persona is the only place sets are declared: a notebook belongs to the voice that keeps it, and an agent already names its persona.

A notebook holds data about a person, so it lives under the world’s normal handling for that: its scope, its retention, its export. It is not session scratch that escapes those rules because some of it happens to be integers.

The declaration is inspectable by the operator. Whoever runs a persona should be able to read what it is keeping, because they answer for it — and because a set is reusable, that should not require reading a realm’s source.

What this spec does not do is enumerate acceptable slots. A coach keeping adherence, a support agent keeping frustration, a pastor keeping how open somebody is: one mechanism, and whether a particular slot suits a particular deployment is the operator’s judgement at grant time rather than a list in this document.