The World Vol 9 · The Village
ch 94 / 105
Chapter 94

Making Something of a Day

Two calls between a day and a thought

Halla's stream is twenty-four rows and every one of them is something that happened. Ander came into sight. She handed him fifty parcels. He took forty out of her hands. She walked two cells and ate for the rest of the afternoon. The last chapter can bring any eight of those to mind for any question, and the chapter before it weighed each one the moment it was written, and between the two of them there is still not a single row that says what any of it meant to her. The fourth kind of memory has been in the schema since the eighth migration, with a column of its own that the other three kinds may not fill, and nothing has ever written one: a thought, drawn from other memories, is the one row a world cannot produce on its own, because a world records what happened and an opinion about it has to be asked for.

Asking for one is the first time in this book that text a model wrote goes into a table the world reads back. Every prompt in the last volume was the operator's, addressed to a model as a piece of equipment, and every answer was printed and dropped. Here the answer is kept, weighed, embedded, and brought to mind by the next question, which means the thing that has been true of every row so far, that its text is a Go format string filled in from an event, stops being true for one kind of row. A reflection is two calls deep, and every memory a statement claims to be drawn from must be one this villager has, at a tick no later than the reflection's, or the statement is refused whole, before anything is written.

On a cadence in ticks, a villager's newest memories are put to the small model with a request for two questions about them, in a schema. Each question is turned into a row of numbers and retrieved against, exactly as the last chapter retrieves, and the eight memories that come to mind are put back to the model with the question and a request for one statement plus the identities of the memories the statement was drawn from.

Those identities are the point. A statement that names its sources can be checked against a stream the engine already holds, one identity at a time, and a statement that fails the check on any one of them is not a statement about this villager's life. It is not trimmed to the citations that check, because a statement drawn from four memories and supported by three is not the statement the model made.

This is the first place in the book where grounding is enforced instead of requested: the prompt asks for citations, and the seam decides what a citation is worth.

The rest is consequences. An accepted statement is appended as a thought with its citations in the column that only a thought may fill, so it is retrievable like any other row and a later reflection may draw on it; a thought drawn from thoughts drawn from thoughts is a chain, and the chain's depth is capped in a file and enforced at the same seam, for a reason the worked failure runs. And a reflection is the most expensive thing a villager does, three calls where a decision is one, and the cadence is chosen against that price and not against how often a person might like to think.

Five numbers in a file and two templates beside them

Everything a reflection runs under is in configs/, in the file the last chapter left room in. The retrieval block is untouched; a second block beside it holds the cadence, the window, the question count, the depth cap and the token limit, and the importance table gains the row for the kind, so that a thought can be weighed by the same integer arithmetic as everything else.

▣ Build · stage 1 — the reflection block, and the table's row for a thought
// configs/thought.json — the retrieval block is the last chapter's; this is the block beside it
  "reflection": {
    "every": 2000,
    "recent": 24,
    "questions": 2,
    "depth": 2,
    "limit": 128
  }
// configs/importance.json — the base table gains one row
    "did": {"meal": 1, "walk": 1, "gift": 3, "take": 3},
    "thought": {"reflection": 4}

Every one of the five is a design choice and the run below prints all five from the file. every is 2000 because that is a day in this village, the two thousand ticks the stream fixture was dumped from, and making something of a day is what a reflection does. recent is 24 so that the whole of Halla's day fits in one prompt; a longer life reads its newest twenty-four. questions is 2, and the price section below is the reason it is not more. depth is 2 and the worked failure is the reason it is not off. limit is the most tokens one answer may run to, and it is not advice: an answer that reaches it is cut off there and, as one below shows, is not an answer at all. A thought starts at 4 on the importance table, above a meal's 1 and level with the base for being taken from, and the table's first-of-its-kind adjustment applies to it like any other row, so the first thought of a life weighs 6 and every one after weighs 4. No grams move in a thought, so no band applies.

The two prompts are files as well, and that is new. The last volume's configs/ deliberately held no prompt: what to say to a model was a program's business. A reflection's prompt is different in one way that matters. It is text an operator may want to read and change without rebuilding the thing that sends it, and it is rendered from stored rows by Go, so putting the template in a file costs nothing that the program's promise depended on: the bytes that go to the model are still bytes the engine produced, from a template the build carries and rows the table holds, and two renderings of the same rows are the same bytes.

▣ Build · stage 2 — the two templates, whole
// configs/reflect-questions.tmpl
{{.Name}} remembers, from the most recent part of the day, in the order it happened:
{{range .Memories}}[{{.ID}}] {{.Text}}
{{end}}
Ask {{.Count}} questions, one line each, about what this day meant to {{.Name}}, that {{.Name}} could answer from these memories alone.
// configs/reflect-statement.tmpl
The question is: {{.Question}}
What {{.Name}} remembers that bears on it, each with its number:
{{range .Memories}}[{{.ID}}] {{.Text}}
{{end}}
Answer for {{.Name}} in one statement, one breath long, about what it meant and never how much, and give the numbers of the memories the statement is drawn from.

Read what is not in them. Neither says that a citation has to exist, or that a memory may only be cited from the list, or that a number the statement claims will be believed. Those are the seam's rules, and a rule stated in two places is a rule that can disagree with itself; the engine's copy is the one that runs, so the prompt does not carry one. What the templates do carry is a request, in the register the memory lines are in: a memory reaches the model as its identity and its line and as nothing else, so the weight, the tick and the about column stay on the engine's side, and the second template asks for a statement about what the day meant and never how much, which is a request and not a guarantee. A page further down shows what a small model does with it.

The Go that reads the block and renders the templates is in the village package, and like everything in that package it opens no connection, reads no clock and calls no model. Due is the cadence as arithmetic on a life. Recent is the window, in the order the day was lived. Render is the standard library's text template with one option set, so that a field the template names and the data does not carry is an error and never the text <no value> arriving at a model.

▣ Build · stage 3 — a cadence, a window, and a rendering
// internal/village/reflect.go

// Reflection is the block of configs/thought.json a villager makes
// something of a day by: how often, in ticks; how many of its newest
// memories the questions are asked about; how many questions one
// reflection asks; how deep a thought may stand above the world; and
// the most tokens one answer may run to.
type Reflection struct {
	Every     int `json:"every"`
	Recent    int `json:"recent"`
	Questions int `json:"questions"`
	Depth     int `json:"depth"`
	Limit     int `json:"limit"`
}

// Due reports whether a villager founded at founded reflects at the
// boundary of tick now: once every Every ticks of its life, counting
// the founding tick as the first.
func (r Reflection) Due(founded, now int) bool {
	lived := now - founded + 1
	return lived > 0 && lived%r.Every == 0
}

// Recent is the newest n of a villager's rows at ticks up to now, in
// the order they were lived: by tick, then by identity inside a tick.
func Recent(rows []Memory, now, n int) []Memory {
	var have []Memory
	for _, m := range rows {
		if m.Tick <= now {
			have = append(have, m)
		}
	}
	sort.SliceStable(have, func(i, j int) bool {
		if have[i].Tick != have[j].Tick {
			return have[i].Tick < have[j].Tick
		}
		return have[i].ID < have[j].ID
	})
	if len(have) > n {
		have = have[len(have)-n:]
	}
	return have
}

// Prompt is what a template is rendered from: whose reflection it is,
// the memories it may name, how many questions are wanted, and, for
// the second call, the question being answered. A memory reaches the
// template as its identity and its line and as nothing else; the
// weight, the tick and the about column stay on the engine's side.
type Prompt struct {
	Name     string
	Memories []Memory
	Count    int
	Question string
}

// Render renders one of the templates configs/ ships with the data
// given. The output is bytes the engine produced from a file and from
// stored rows, so two renderings of the same data are the same bytes
// and a sha256 over them means something.
// ...
func Render(name string, data Prompt) ([]byte, error) {
	b, err := configs.Files.ReadFile(name)
	if err != nil {
		return nil, fmt.Errorf("village: %w", err)
	}
	t, err := template.New(name).Option("missingkey=error").Parse(string(b))
	if err != nil {
		return nil, fmt.Errorf("village: %s: %w", name, err)
	}
	var out bytes.Buffer
	if err := t.Execute(&out, data); err != nil {
		return nil, fmt.Errorf("village: %s: %w", name, err)
	}
	return out.Bytes(), nil
}
$ go run ./cmd/reflect -mode settings
reflect: what a reflection runs under, read out of the files the build embeds

  the cadence    every 2000 ticks of a life, off configs/thought.json
  the questions  2, asked about the newest 24 memories
  the depth cap  2, off the same file
  the retrieval  half-life 250, weights 1.00 1.00 3.00, k 8, off the same file
  the model      qwen2.5:0.5b-instruct-q4_K_M, pinned at c5396e06af294bd1
  the embedding  nomic-embed-text:v1.5, pinned at 970aa74c0a90ef74
  the answer     at most 128 tokens, temperature 0.00, a seed of the tick and the body
  the templates  configs/reflect-questions.tmpl, configs/reflect-statement.tmpl

  the importance table's row for the kind, off configs/importance.json
  kind     event       base
  thought  reflection     4
  plus 2 for the first thought of a life, and no grams band, because no grams move in one

  configs/reflect-questions.tmpl, 268 bytes, sha256 d11f5ebbed9a5efb
    | {{.Name}} remembers, from the most recent part of the day, in the order it happened:
    | {{range .Memories}}[{{.ID}}] {{.Text}}
    | {{end}}
    | Ask {{.Count}} questions, one line each, about what this day meant to {{.Name}}, that {{.Name}} could answer from these memories alone.

  configs/reflect-statement.tmpl, 305 bytes, sha256 d562b9492cbab36c
    | The question is: {{.Question}}
    | What {{.Name}} remembers that bears on it, each with its number:
    | {{range .Memories}}[{{.ID}}] {{.Text}}
    | {{end}}
    | Answer for {{.Name}} in one statement, one breath long, about what it meant and never how much, and give the numbers of the memories the statement is drawn from.

  neither template says which memories may be cited, and neither says a
  citation has to exist: that is the seam's rule, and a rule stated in two
  places is a rule that can disagree with itself

That mode runs on a bare workstation with no containers anywhere and opens nothing. Two hundred and sixty-eight bytes and three hundred and five, each with a sha256, so that a rendering further down can be traced to the template that made it. The model and the embedding model are the last volume's, at the last volume's digests, read out of the kit and never typed; the temperature is nought, so an answer is a function of the seed, and the seed is the tick and the body deciding, added, which is the rule every call in this volume is under. A reflection is due once every 2000 ticks of a life, counting the founding tick as the first, so a villager founded at tick 901 reflects at the boundary of tick 2900, which is the last tick of the day the stream records.

Now the first prompt, rendered for Halla at that boundary out of the committed stream. The mode prints the whole of it, because a prompt is bytes the reader's own code produced and the reader should see them once; then it holds the bytes against a recording made from the live model, which is this chapter's fixture and is explained in a moment.

▣ Build · stage 4 — the first prompt, whole, and what it came to
$ go run ./cmd/reflect -mode prompt | tail -38
  Halla's newest 24 rows at ticks up to 2900: 24 the world wrote and 0 thoughts
  the prompt, 1168 bytes, sha256 447a9ab39467fff8
    | Halla remembers, from the most recent part of the day, in the order it happened:
    | [1] Ander came into sight, 0.8 cells off
    | [2] Mose came into sight, 0.8 cells off
    | [8] handed Ander 50 parcels, 25.0 grams
    | [11] Ander took 40 parcels out of Halla's hands, 20.0 grams
    | [13] handed Ander 50 parcels, 25.0 grams
    | [17] handed Ander 37 parcels, 18.5 grams
    | [25] Ander came into sight, 0.8 cells off
    | [28] went from 8,5 to 9,3
    | [35] ate 50 mouthfuls on 9,3, 25.0 grams
    | [38] ate 50 mouthfuls on 9,3, 25.0 grams
    | [41] ate 50 mouthfuls on 9,3, 25.0 grams
    | [44] ate 50 mouthfuls on 9,3, 25.0 grams
    | [47] ate 50 mouthfuls on 9,3, 25.0 grams
    | [50] ate 50 mouthfuls on 9,3, 25.0 grams
    | [53] ate 50 mouthfuls on 9,3, 25.0 grams
    | [56] ate 50 mouthfuls on 9,3, 23.5 grams
    | [63] ate 27 mouthfuls on 9,3, 9.3 grams
    | [62] Ander came into sight, 0.2 cells off
    | [69] handed Ander 50 parcels, 25.0 grams
    | [73] handed Ander 17 parcels, 8.5 grams
    | [83] ate 2 mouthfuls on 9,3, 1.0 grams
    | [84] went from 9,3 to 9,2
    | [86] ate 50 mouthfuls on 9,2, 25.0 grams
    | [87] ate 8 mouthfuls on 9,2, 4.0 grams
    |
    | Ask 2 questions, one line each, about what this day meant to Halla, that Halla could answer from these memories alone.

  against the recording, exemplars/reflections.json
    rendered here                  1168 bytes  sha256 447a9ab39467fff8
    recorded                       1168 bytes  sha256 447a9ab39467fff8
    the same bytes; the recording is a recording of this template and this stream
    575 tokens in by the model's own table, 29 out, stopped because: stop

  what the model answered, once, and yours will differ:
    | { "questions": [ "What did Ander do in the morning?", "What did Mose do in the afternoon?" ] }

Eleven hundred and sixty-eight bytes, twenty-four of Halla's rows in the order she lived them, and not one number on the engine's side of the line: no weight, no tick, no about column, only the identity in brackets and the line. Rows 63 and 62 come in that order because the window is sorted by the tick the thing happened at and not by the number the row was given, which the eighth migration's chapter explained. The 575 tokens are what the model's own table made of those bytes, counted by the server, and they are the price of reading a day; the 29 tokens back are two questions in a schema. The two questions are a model's and are labelled so. One asks about a morning this world does not have. A question is one line the model chose; what it costs and what is done with it are the engine's, and those are what the rest of the page compares.

One more field on the request

A question has to come back as a question and a statement as a statement plus a list of identities, and free text cannot be relied on for either. The server this world runs takes a schema on a generation request and steers what the model produces toward it, and the client the last volume wrote gains that as one field on Ask. Then one helper on this side reads the answer into a Go value, strictly, because a schema on the request is a request and what arrives is bytes a model chose.

▣ Build · stage 5 — the schema on the wire, and the answer read strictly
// internal/lang/serve.go — Ask, with the field this volume adds; the seven before it are the last volume's
type Ask struct {
	Model  string
	Prompt string
	// ...
	Limit int
	// ...
	Context int
	// ...
	Temp float64
	// ...
	Seed int

	// Format is the schema the answer must match, as the server takes
	// one, or nothing for an answer in free text. It is a request and
	// not a guarantee: the server steers what it produces toward the
	// schema, and what came back is still parsed on this side before
	// anything reads a field out of it, by Shaped. Nothing in this
	// world reads a model's answer any other way once a schema is on
	// the request.
	Format json.RawMessage
}

// generateReq — the wire gains the same field, left out when empty
type generateReq struct {
	Model   string          `json:"model"`
	Prompt  string          `json:"prompt"`
	Stream  bool            `json:"stream"`
	Format  json.RawMessage `json:"format,omitempty"`
	Options genOptions      `json:"options"`
}
// internal/lang/shaped.go

// Shaped reads one answer into the value it was asked to match, and
// refuses an answer that does not parse as exactly that: not JSON at
// all, a field the shape does not have, or anything after the object.
//
// It is the only road from a model's text to a Go value in this world,
// and it is strict on purpose. A schema on the request is a request;
// what arrives is bytes a model chose, and the place to find out that
// they are not the shape asked for is here, before a field is read,
// rather than in whatever would have read it. A refusal here is a
// refusal of the whole answer: nothing is salvaged from an answer that
// did not parse.
func Shaped(s Said, into any) error {
	dec := json.NewDecoder(bytes.NewReader([]byte(s.Text)))
	dec.DisallowUnknownFields()
	if err := dec.Decode(into); err != nil {
		return fmt.Errorf("lang: %s answered with something that is not the shape asked for: %w", s.Model, err)
	}
	if dec.More() {
		return fmt.Errorf("lang: %s answered with more than one value", s.Model)
	}
	return nil
}
// internal/village/reflect.go — the two shapes, and the schemas the server is sent

// Asked is the shape the first call answers in: the questions, one
// line each, and nothing else.
type Asked struct {
	Questions []string `json:"questions"`
}

// Drawn is the shape the second call answers in: one statement, and
// the identities of the memories it claims to be drawn from.
type Drawn struct {
	Statement string  `json:"statement"`
	DrawnFrom []int64 `json:"drawn_from"`
}

// AskedSchema is the schema the first answer must match, written out
// for a server that takes one: an object with one field, an array of
// exactly n strings.
func AskedSchema(n int) json.RawMessage {
	return json.RawMessage(fmt.Sprintf(`{"type":"object","properties":{"questions":{"type":"array","items":{"type":"string"},"minItems":%d,"maxItems":%d}},"required":["questions"]}`, n, n))
}

// DrawnSchema is the schema the second answer must match: an object
// with a statement and an array of integers.
var DrawnSchema = json.RawMessage(`{"type":"object","properties":{"statement":{"type":"string"},"drawn_from":{"type":"array","items":{"type":"integer"}}},"required":["statement","drawn_from"]}`)
$ go test ./internal/lang/ -run 'TestTheSchemaGoesOutOnTheWireAndOnlyWhenThereIsOne|TestAnAnswerThatIsNotTheShapeAskedForIsRefusedWhole' -v
=== RUN   TestTheSchemaGoesOutOnTheWireAndOnlyWhenThereIsOne
    shaped_test.go:40: the schema went out as given, and a request without one sent no format field
--- PASS: TestTheSchemaGoesOutOnTheWireAndOnlyWhenThereIsOne (0.00s)
=== RUN   TestAnAnswerThatIsNotTheShapeAskedForIsRefusedWhole
    shaped_test.go:76: one shape accepted and 4 ways of not being it refused, each naming the model
--- PASS: TestAnAnswerThatIsNotTheShapeAskedForIsRefusedWhole (0.00s)
PASS
ok  	theworld/internal/lang	0.005s

The field is a raw message and not a string, so what the caller hands over is what goes on the wire, byte for byte, and the first test reads the request the forty-line fake server recorded and finds the schema in it as given; the same test sends a request with no schema and finds no format field at all, because an empty field is not the same thing as an absent one and a server handed an empty schema has an opinion about it. The second test is the four ways an answer can fail to be the shape: prose, a field the shape does not have, a field of the wrong type, and two values where one was asked for. Every one is refused whole and the refusal names the model, because the model is the thing that produced it.

DisallowUnknownFields is the line to read twice. A model that answers with the statement, the citations and a helpful "confidence": 0.9 has answered with something the seam has no column for, and an answer that carries a field nothing asked for is an answer that was not shaped by the request. Refusing it costs one answer. Reading round it would mean a decision somewhere about which extra fields are harmless, and that decision would be made by whoever wrote the last reader.

Eight citations, checked one at a time

The check is a function of three things the engine holds: this villager's rows, the tick the reflection is at, and the identities the statement claims. It is pure, it is in the village package beside the rules for weighing and retrieving, and it says no for exactly four reasons. A citation names a memory this villager does not have, which is the same refusal whether the memory belongs to somebody else or to nobody, because from inside one stream those are the same fact. A citation names a memory at a tick after the reflection's, which the fixture cannot produce on its own and a perturbation below does. A memory is cited twice, because a list of sources with a source in it twice is not the list the check was asked about, and collapsing it would be repairing the answer. And the statement is drawn from nothing. After those four comes the depth.

▣ Build · stage 6 — the seam's one check, the depth under it, and the row it makes
// internal/village/reflect.go

// Verdict is what the seam says about one statement: accepted, or
// refused by a named check for a stated reason. Depth is how high the
// thought would stand above the world if it were written, worked out
// whether or not it is.
type Verdict struct {
	OK     bool
	Check  string
	Reason string
	Depth  int
}

// Depth is how many thoughts stand between a memory and the world. A
// memory of something seen, done or heard is at depth 0; a thought is
// one deeper than the deepest memory it was drawn from. A thought
// drawn from nothing but the world is at depth 1.
func Depth(by map[int64]Memory, m Memory) int {
	if m.Kind != Thought {
		return 0
	}
	deepest := 0
	for _, id := range m.DrawnFrom {
		if d := Depth(by, by[id]); d > deepest {
			deepest = d
		}
	}
	return deepest + 1
}

// Cite is the check a statement passes before it becomes a row, and
// it runs before anything is written. rows are this villager's rows
// and nobody else's; now is the tick the reflection is at; cites are
// the identities the statement claims to be drawn from.
// ...
func Cite(rows []Memory, now int, cites []int64, cap int) Verdict {
	by := map[int64]Memory{}
	for _, m := range rows {
		by[m.ID] = m
	}
	if len(cites) == 0 {
		return Verdict{Check: "cites", Reason: "a statement drawn from nothing"}
	}
	seen := map[int64]bool{}
	for _, id := range cites {
		m, ok := by[id]
		if !ok {
			return Verdict{Check: "cites", Reason: fmt.Sprintf("memory %d is not one this villager has", id)}
		}
		if m.Tick > now {
			return Verdict{Check: "cites", Reason: fmt.Sprintf("memory %d is at tick %d, which is after tick %d", id, m.Tick, now)}
		}
		if seen[id] {
			return Verdict{Check: "cites", Reason: fmt.Sprintf("memory %d is cited twice", id)}
		}
		seen[id] = true
	}
	would := Memory{Kind: Thought, DrawnFrom: cites}
	d := Depth(by, would)
	if cap > 0 && d > cap {
		return Verdict{Check: "depth", Reason: fmt.Sprintf("a thought at depth %d, and the cap is %d", d, cap), Depth: d}
	}
	return Verdict{OK: true, Depth: d}
}

// Reflected is the event an accepted reflection is appended as: at the
// reflection's tick, of the thought kind, weighed on the table's
// reflection row, with the statement as its line, about every identity
// the cited memories are about, and drawn from the citations in
// ascending order.
// ...
func Reflected(rows []Memory, now int, statement string, cites []int64) Event {
	by := map[int64]Memory{}
	for _, m := range rows {
		by[m.ID] = m
	}
	set := map[sim.EntityID]bool{}
	for _, id := range cites {
		for _, who := range by[id].About {
			set[who] = true
		}
	}
	about := make([]sim.EntityID, 0, len(set))
	for who := range set {
		about = append(about, who)
	}
	sort.Slice(about, func(i, j int) bool { return about[i] < about[j] })
	from := append([]int64(nil), cites...)
	sort.Slice(from, func(i, j int) bool { return from[i] < from[j] })
	return Event{
		Tick: now, Kind: Thought, What: "reflection", Text: statement,
		About: about, DrawnFrom: from,
	}
}
$ go test ./internal/village/ -run 'TestAReflectionIsDueOnceEveryCadenceOfALife|TestTheNewestMemoriesAreTakenByTickThenIdentity|TestACitationHasToExistBelongAndBeNoLaterThanTheReflection|TestAStatementIsRefusedWholeAndNeverTrimmed|TestDepthIsOneMoreThanTheDeepestCitation|TestTheCapRefusesADeeperThoughtAndTheCapOffDoesNot|TestAThoughtRowIsAboutWhatItsCitationsAreAbout|TestARenderingIsAFunctionOfTheTemplateAndTheRows|TestNeitherTemplateCarriesTheSeamsRule|TestTheShippedReflectionIsACadenceAWindowACountACapAndALimit|TestTheVillageStillImportsNeitherTheModelClientNorTheStore' -v
=== RUN   TestAReflectionIsDueOnceEveryCadenceOfALife
--- PASS: TestAReflectionIsDueOnceEveryCadenceOfALife (0.00s)
=== RUN   TestTheNewestMemoriesAreTakenByTickThenIdentity
--- PASS: TestTheNewestMemoriesAreTakenByTickThenIdentity (0.00s)
=== RUN   TestACitationHasToExistBelongAndBeNoLaterThanTheReflection
--- PASS: TestACitationHasToExistBelongAndBeNoLaterThanTheReflection (0.00s)
=== RUN   TestAStatementIsRefusedWholeAndNeverTrimmed
    reflect_test.go:112: four citations, three of which check, is refused; the three alone are accepted; nothing trims the four to the three
--- PASS: TestAStatementIsRefusedWholeAndNeverTrimmed (0.00s)
=== RUN   TestDepthIsOneMoreThanTheDeepestCitation
--- PASS: TestDepthIsOneMoreThanTheDeepestCitation (0.00s)
=== RUN   TestTheCapRefusesADeeperThoughtAndTheCapOffDoesNot
--- PASS: TestTheCapRefusesADeeperThoughtAndTheCapOffDoesNot (0.00s)
=== RUN   TestAThoughtRowIsAboutWhatItsCitationsAreAbout
    reflect_test.go:183: the first thought of a life weighs 6, 4 base and 2 first; the second weighs 4
--- PASS: TestAThoughtRowIsAboutWhatItsCitationsAreAbout (0.00s)
=== RUN   TestARenderingIsAFunctionOfTheTemplateAndTheRows
--- PASS: TestARenderingIsAFunctionOfTheTemplateAndTheRows (0.00s)
=== RUN   TestNeitherTemplateCarriesTheSeamsRule
--- PASS: TestNeitherTemplateCarriesTheSeamsRule (0.00s)
=== RUN   TestTheShippedReflectionIsACadenceAWindowACountACapAndALimit
    reflect_test.go:246: every 2000 ticks, 2 questions about the newest 24, a cap of 2, at most 128 tokens an answer
--- PASS: TestTheShippedReflectionIsACadenceAWindowACountACapAndALimit (0.00s)
=== RUN   TestTheVillageStillImportsNeitherTheModelClientNorTheStore
    reflect_test.go:259: every file of the package read, and none of them names the model client or the store
--- PASS: TestTheVillageStillImportsNeitherTheModelClientNorTheStore (0.00s)
PASS
ok  	theworld/internal/village	0.007s

No later than is the line the check draws in time, and it is the line the last chapter's retrieval draws: a row at the reflection's own tick can come to mind, so it can be cited, and a row after it cannot be either. The tick the thought is written at is the reflection's, so a thought and the memories it was drawn from can share a tick, and a reflection reads the stream as it stood when it began, which means the second question of one reflection cannot draw on the first question's answer. What one reflection wrote is there for the next one.

Reflected is where every column of the row but one is decided by the engine. The tick is the reflection's. The kind is thought and the table's row for it decides the weight. The about column is the union of the cited rows' about columns, in ascending order, so a thought drawn from rows about Ander is about Ander whatever its text says and a thought drawn from meals is about nobody; it is never read off the text, for the reason the eighth migration's chapter gave about finding Ander inside Anders. The citations are sorted, so that two statements citing the same eight rows in a different order make the same row. The one column a model wrote is the text, and nothing downstream of this line reads a fact out of it. The third test from the bottom is what keeps the templates honest: it reads both files and fails on the words a rule would be stated in.

The last test walks the whole package and finds it still names neither the model client nor the store. The last chapter's walk over the same directory logged how many files it read, and the eighth file this chapter adds moved that line; the fix is the one the migrations listing got two chapters ago, at source: the earlier test walks the seven files it shipped, by name, and this one walks the directory and logs no count.

Now a reflection, end to end. It replays from a recording, and the recording is the third fixture this volume commits and the first of its kind: exemplars/reflections.json, twenty-one proposals produced once by the small model at the settings above, through the server this world runs, with the row the embedding model gave every question and every accepted thought, so that the whole cycle, retrieval included, can be run over it with no model server anywhere. What the model wrote is in the file and is labelled, on the page and in the file's own note. What is not in the file is any verdict: the recording holds prompts' hashes and answers' bytes, and everything between them is computed here, every time, by the code above.

▣ Build · stage 7 — the cycle, and Halla's first question replayed through it
// cmd/reflect/cycle.go — the driver; the calls it makes are the last volume's, through
// an oracle that is the recording on this page and a live server further down

// reflect1 runs one reflection for one villager at one tick, in four
// phases: the questions call; every question embedded; for every
// question a retrieval, the statement call and the seam; and every
// accepted thought embedded and appended to the stream with its row.
//
// Four phases and not two loops, because the server holds one model
// resident at a time and every change of model is a load: the calls
// are grouped by the model that answers them, so a reflection changes
// model three times and not once a call.
//
// Nothing a model says reaches the stream except through Cite, and
// nothing reaches Cite that Shaped did not parse first.
func reflect1(st settingsOf, s *stream, who string, now, cap int, o oracle) (result, error) {
	// ...
	seed := seedFor(now, p.Body.ID)
	// The stream as it stands when the reflection begins. A thought
	// this reflection writes is not in it, so a second question cannot
	// draw on the first question's answer: one reflection reads one
	// day, and what it wrote is there for the next one.
	mine := upTo(s.mine(who), now)
	r := result{who: who, now: now, rows: len(mine), recent: village.Recent(mine, now, st.r.Recent)}

	// The first call: the newest memories, and a request for questions.
	qp, err := village.Render(configs.ReflectQuestions, village.Prompt{Name: who, Memories: r.recent, Count: st.r.Questions})
	// ...
	a, err := o.ask("questions", 0, qp, village.AskedSchema(st.r.Questions), seed)
	// ...
	var got village.Asked
	if err := lang.Shaped(lang.Said{Model: st.kit.Models[lang.Small].Name, Text: a.text}, &got); err != nil {
		r.asked.shaped = err
		return r, nil
	}
	// ...
	// The second call, once a question: bring memories to mind,
	// render, ask, and check. Nothing is written in this phase.
	for i, q := range got.Questions {
		d := &r.drawn[i]
		scored, err := st.ret.Retrieve(now, mine, s.vec, vecs[i], false)
		// ...
		d.top = st.ret.Top(scored)
		// ...
		a, err := o.ask("statement", i+1, sp, village.DrawnSchema, seed)
		// ...
		if err := lang.Shaped(lang.Said{Model: st.kit.Models[lang.Small].Name, Text: a.text}, &d.answer); err != nil {
			d.shaped = err
			continue
		}
		// The seam. Every citation against this villager's rows and
		// nobody else's, at this tick, and the depth of the thought
		// the statement would become.
		d.verdict = village.Cite(mine, now, d.answer.DrawnFrom, cap)
	}
	// ...
}
$ go run ./cmd/reflect -mode reflect | tail -53 | head -33
  the first call: 1168 bytes of prompt, sha256 447a9ab39467fff8, 575 tokens in, 29 out, stopped because: stop
  the questions, which a model wrote and yours will differ:
    1. What did Ander do in the morning?
    2. What did Mose do in the afternoon?

  question 1, embedded, and Halla's 24 rows at ticks up to 2900 ranked against it
  rank   id   tick weight  recency import   relev   score  kind     text
     1   11   1015     10   0.0054   1.00  0.5611  2.6887  saw      Ander took 40 parcels out of Ha...
     2   86   2883      3   0.9540   0.30  0.3764  2.3830  did      ate 50 mouthfuls on 9,2, 25.0 g...
     3   84   2781      1   0.7190   0.10  0.4870  2.2801  did      went from 9,3 to 9,2
     4    1    901      4   0.0039   0.40  0.6158  2.2513  saw      Ander came into sight, 0.8 cell...
     5   87   2899      1   0.9972   0.10  0.3798  2.2367  did      ate 8 mouthfuls on 9,2, 4.0 grams
     6   62   2203      2   0.1448   0.20  0.6170  2.1958  saw      Ander came into sight, 0.2 cell...
     7   73   2341      4   0.2123   0.40  0.4838  2.0638  did      handed Ander 17 parcels, 8.5 grams
     8   25   1337      2   0.0131   0.20  0.6158  2.0605  saw      Ander came into sight, 0.8 cell...
  the second call: 602 bytes of prompt, sha256 2d2453b788d9ecc5, 245 tokens in, 64 out, stopped because: stop
    | The question is: What did Ander do in the morning?
    | What Halla remembers that bears on it, each with its number:
    | [11] Ander took 40 parcels out of Halla's hands, 20.0 grams
    | [86] ate 50 mouthfuls on 9,2, 25.0 grams
    | [84] went from 9,3 to 9,2
    | [1] Ander came into sight, 0.8 cells off
    | [87] ate 8 mouthfuls on 9,2, 4.0 grams
    | [62] Ander came into sight, 0.2 cells off
    | [73] handed Ander 17 parcels, 8.5 grams
    | [25] Ander came into sight, 0.8 cells off
    |
    | Answer for Halla in one statement, one breath long, about what it meant and never how much, and give the numbers of the memories the statement is drawn from.
  the statement, which a model wrote and yours will differ:
    | Ander took 40 parcels out of Halla's hands, 20.0 grams.
  drawn from, as the model gave them   11, 86, 84, 1, 87, 62, 73, 25
  the seam                             accepted, at depth 1
  the row appended                     id 89, tick 2900, thought, weight 6 (4 base + 2 first), about 1 Halla, 2 Ander, drawn from 1, 11, 25, 62, 73, 84, 86, 87

Read the ranking first, because it is the last chapter's arithmetic over a vector the fixture holds for a question the model asked, and it replays to the digit: asked what Ander did, the eight that come to Halla's mind are the taking, two meals and a walk carried by recency, and three sightings of him and one gift carried by cosines above 0.48. The second prompt is those eight in rank order with the question on top, 602 bytes, 245 tokens, and its sha256 is the check that the retrieval replayed, because a different ranking renders different bytes. The statement is a model's, and it is the third line of the prompt, row 11, copied out with a full stop, numbers and all. The prompt asked for what the day meant and never how much, and a half-billion-parameter model answered with the line that scored highest, which is a fact about this model at this size and not a fault in the check. The check has nothing to say about the words. It has eight identities to check, finds every one in Halla's stream at a tick no later than 2900 and none of them twice, works out that a thought drawn from eight memories the world wrote stands at depth 1, and says yes.

Then the row. Identity 89, the next after the day's 88, because the stream numbers a thought the way the database will. Tick 2900, the reflection's. Weight 6, which is the table's 4 for a reflection plus 2 for the first thought of Halla's life. About Halla and Ander, read off the eight cited rows and off nothing else. Drawn from the eight, sorted. Every one of those columns is the engine's and every one is compared; the text between them is the model's and is not.

▣ Build · stage 8 — the second question, refused on its ninth citation
$ go run ./cmd/reflect -mode reflect | tail -19
  question 2, embedded, and Halla's 24 rows at ticks up to 2900 ranked against it
  rank   id   tick weight  recency import   relev   score  kind     text
     1   86   2883      3   0.9540   0.30  0.3904  2.4250  did      ate 50 mouthfuls on 9,2, 25.0 g...
     2   11   1015     10   0.0054   1.00  0.4720  2.4213  saw      Ander took 40 parcels out of Ha...
     3   87   2899      1   0.9972   0.10  0.3948  2.2817  did      ate 8 mouthfuls on 9,2, 4.0 grams
     4   84   2781      1   0.7190   0.10  0.4603  2.2000  did      went from 9,3 to 9,2
     5    2    901      2   0.0039   0.20  0.6182  2.0586  saw      Mose came into sight, 0.8 cells...
     6   83   2776      1   0.7091   0.10  0.4004  2.0104  did      ate 2 mouthfuls on 9,3, 1.0 grams
     7   73   2341      4   0.2123   0.40  0.4141  1.8546  did      handed Ander 17 parcels, 8.5 grams
     8    8    999      7   0.0051   0.70  0.3800  1.8451  did      handed Ander 50 parcels, 25.0 g...
  the second call: 597 bytes of prompt, sha256 b90edc6025d370ce, 251 tokens in, 58 out, stopped because: stop
  the statement, which a model wrote and yours will differ:
    | Mose came into sight, 0.8 cells off
  drawn from, as the model gave them   86, 11, 87, 84, 2, 73, 8, 8, 83
  the seam                             refused by cites: memory 8 is cited twice
  the row appended                     none; nothing was written

  3 calls read off the recording, 3 prompts rendered here and every one the
  recorded bytes, 2 statements checked, 1 thoughts written: 88 rows before, 89 after

Asked what Mose did, the model copied the one row about Mose and cited nine memories for it, eight of them the ones it was shown and one of them, row 8, twice. Eight citations check. The ninth is the eighth again, and the statement is refused whole on it, with nothing written and the stream still at 89 rows. This is the rule in the bolded sentence meeting its first real answer, and the answer is a good one to meet it on, because the temptation is obvious: drop the duplicate and keep a statement that would otherwise pass. The check does not, for the reason a statement drawn from four memories and supported by three is not the statement the model made. A list of sources is what the model said it drew on. If the list is wrong the statement is unsupported, and an unsupported statement in a memory table is a fact nobody witnessed.

The last two lines are the accounting for the recording. Three calls were read off it, three prompts were rendered here and each was the recorded bytes, so the recording is a recording of this template, this stream and this ranking; two statements were checked and one thought written. A recording whose prompts did not match would stop the replay at the first mismatch rather than carry on with answers to questions this code did not ask.

Two of the check's four reasons cannot occur on the fixture as recorded, because the model, shown eight identities, copied identities, and because a reflection at the end of a day has nothing after it to cite. So the bench takes the accepted statement above and perturbs its citations itself, three ways, and says so: a check that has only ever been seen saying yes has not been seen.

▣ Build · stage 9 — three citations that do not check
$ go run ./cmd/reflect -mode refuse | tail -16
  the first citation swapped for 989, which no stream holds
    drawn from   989, 86, 84, 1, 87, 62, 73, 25, at tick 2900
    the seam     refused by cites: memory 989 is not one this villager has
    written      nothing: 88 rows before, 88 after; the statement is not trimmed to the 7 that check

  the first citation swapped for 3, which is Mose's
    drawn from   3, 86, 84, 1, 87, 62, 73, 25, at tick 2900
    the seam     refused by cites: memory 3 is not one this villager has
    written      nothing: 88 rows before, 88 after; the statement is not trimmed to the 7 that check

  the same citations, with the reflection moved back to tick 2898
    drawn from   11, 86, 84, 1, 87, 62, 73, 25, at tick 2898
    the seam     refused by cites: memory 87 is at tick 2899, which is after tick 2898
    written      nothing: 88 rows before, 88 after; the statement is not trimmed to the 7 that check

  3 statements refused whole, 0 rows written, and the one that checks is the one above

Row 989 is nobody's and row 3 is Mose's, and the refusal is the same sentence for both, on purpose. The check is handed this villager's rows and no others, so a memory another villager has is, from where the check stands, a memory that does not exist, and a refusal that said that is Mose's would be a refusal that had looked at Mose's stream to say it. The third case moves the reflection back two ticks and leaves the citations alone, and row 87, the eight mouthfuls at tick 2899, is now a memory of something that has not happened yet. Every one of the three keeps the seven citations that check and is refused on the one that does not, and the row count is the proof: 88 before, 88 after, three times. The bench stops with a non-zero status if any of the three is accepted.

One more refusal happens before the seam is reached at all, and it is the honest half of the schema section. Ander's first statement, in the same recording, ran to the limit.

▣ Build · stage 10 — an answer cut off at 128 tokens, refused before the seam
$ go run ./cmd/reflect -mode reflect -who Ander | tail -34 | head -1
  the second call: 621 bytes of prompt, sha256 c865a6a6a3162284, 276 tokens in, 128 out, stopped because: length
$ go run ./cmd/reflect -mode reflect -who Ander | tail -21 | head -1
  refused before the seam: lang: qwen2.5:0.5b-instruct-q4_K_M answered with something that is not the shape asked for: unexpected EOF

length is the server's own word for having reached the limit, and an answer stopped there is JSON with no closing brace, which Shaped reports as an unexpected end and refuses. The statement it was building was eight memory lines copied one after another with their numbers in, which is what a small model does with a question about breakfast and eight rows about meals, and 128 tokens is where this world stops paying for it. A limit is a price cap, and a price cap that is never reached is a limit that was never tested. The reflection went on to its second question, whose statement passed, and Ander has one thought at the end of his day, as Halla does.

Two calls between a day and a thought, and the seam between the second call and the row A vertical flow. At the top, the stream: this villager's rows at ticks up to now. An arrow to the newest twenty-four, rendered into the first prompt from a template, which goes to the small model and comes back as two questions in a schema. Each question is embedded and retrieved against the stream, and the eight that come to mind are rendered into the second prompt, which goes to the small model and comes back as one statement and its citations. Below that a box labelled the seam lists four checks: every citation is this villager's, none is later than now, none is cited twice, and the depth is within the cap; refused whole to the left, accepted to the right. Accepted becomes a thought row, whose columns are listed as the engine's except the text, and an arrow carries the row back up into the stream, which is the loop the chain runs on. TWO CALLS BETWEEN A DAY AND A THOUGHT the stream this villager's rows at ticks up to now the newest 24 rendered from a template: identity and line only call 1: the small model a schema, a limit, temp 0, seed: the tick and the body two questions a model's words; each one embedded and retrieved the 8 that come to mind rendered with the question, in rank order call 2: the small model one call a question, a statement's schema a statement + citations parsed strictly by Shaped; not the shape: refused here THE SEAM: Cite, over this villager's rows and now every citation is a memory this villager has none is later than now; none is cited twice; at least one the thought would stand no deeper than the cap refused whole; nothing written a thought row tick, kind, weight, about, drawn_from: the engine's text: the model's, and read by nothing back into the stream
Figure 94.1 — what a reflection is made of. Two calls, each a prompt the engine rendered and an answer parsed strictly; one check between the second answer and the table, over rows the engine already holds; and a row whose every column but one is the engine's. The arrow up the right side is the loop the chain runs on.

Seventy-five ticks to make row 89

Everything above ran over two files with no model server anywhere, which is what makes it replay. Now the same reflection against the live models, with the stream in the tables rather than in a fixture, and a clock on every call. The committed day goes into an empty database first, the way the last chapter put it there; the reflection is driven over rows read back ORDER BY tick, id and vectors read back ORDER BY memory; and an accepted thought goes through the same Remember every row of the day went through and its vector through the same COPY. The small model is put in memory before the clock starts, as the first chapter of this volume warmed each model with a call it did not time, so that the first call is a call and not a load. The changes of model inside the reflection are timed, because they are the reflection's own price.

⚙ Tool — the model server, one model resident at a time

The server is Ollama in a podman container on the closed bridge the last volume built, started with OLLAMA_MAX_LOADED_MODELS=1, holding the three model files on a named volume; the Go code runs in world-go on the same bridge and the database in world-db, which is the only container with psql. Nothing below downloads anything. The schema on a generation request is the server's format field, documented under its API reference at github.com/ollama/ollama; the client sends it as given and reads nothing back but the fields the last volume settled on.

▣ Build · stage 11 — one reflection against the live models, priced
$ podman kill world-db && podman start world-db
world-db
world-db
$ podman exec -w /bench world-go go run ./cmd/reflect -mode live | tail -63 | head -8
  the day into the tables: 88 rows appended, 88 given the identity the dump carries, 88 vectors copied
  read back: 88 rows and 88 vectors, over 3 people

  the small model loaded before the clock starts, at 4096 tokens of context

  the first call: 1168 bytes of prompt, sha256 447a9ab39467fff8, 575 tokens in, 29 out, stopped because: stop
  the questions, which a model wrote and yours will differ:
    1. What did Ander do in the morning?
$ podman exec -w /bench world-go go run ./cmd/reflect -mode live | tail -19
  Halla's stream read back, ORDER BY tick, id: 25 rows, and the thoughts among them
    id   tick kind     weight  about        drawn from
    89   2900 thought       6  1 Halla, 2 Ander 1, 11, 25, 62, 73, 84, 86, 87
  1 thought rows written, 1 given the identity the stream gave them, 0 memories with no vector

  what one reflection cost, at 10 ticks a second (measured here; yours will differ)
       call                   model       took    ticks
    1  the questions          small     2.138s     21.4
    2  question 1 embedded    embed      424ms      4.2
    3  question 2 embedded    embed       25ms      0.3
    4  statement 1            small     2.727s     27.3
    5  statement 2            small     1.727s     17.3
    6  thought 1 embedded     embed      435ms      4.3
  one reflection, 3 calls and 3 embeddings        7.476s     74.8 ticks
  the resident model changed 3 times inside it, and every change is a load
  nomic-embed-text:v1.5 is the model resident now, answering on the processor, with no bytes on a graphics card
  24 people, one worker, one reflection each   2m59.421s     1794 ticks
  the cadence is every 2000 ticks, so the village spends 90% of it reflecting
  and not one number the models answered is on this page
$ podman exec world-db psql -U world -d world -c "SELECT id, villager, tick, kind, weight, about, drawn_from FROM memory WHERE kind = 'thought' ORDER BY id;"
 id | villager | tick |  kind   | weight | about |        drawn_from
----+----------+------+---------+--------+-------+--------------------------
 89 | Halla    | 2900 | thought |      6 | {1,2} | {1,11,25,62,73,84,86,87}
(1 row)

The first prompt is the same 1168 bytes with the same sha256 and the same 575 tokens as the replay, which is the one thing about a live run that has to be identical: the prompt is rendered from the tables and the tables hold the fixture. The questions came back as the same words too, and that is a fact about this machine, this build and a seeded answer at temperature nought, not a check; on yours they will differ, and so will everything downstream of them, and the seam will judge whatever arrives by the same rule. What is compared is the shape of the outcome: one thought written, given by the server the identity the stream gave it, 89, and no memory in either table without a vector. The row the database holds is the row the replay made, column for column, and the query above shows it with every column but its text, which is the model's.

Then the price, which is the reason the cadence is what it is. Every duration in that block was measured here, on the eight-core AMD Ryzen 7 3700X with 30 GB of memory that every duration in this volume has been measured on, rootless podman, with the models answering on the processor and no graphics card in use, and the line naming the resident model is the server's own answer to which piece of hardware did the work. The pace is the one this volume runs at, ten ticks a second, a tick of a hundred milliseconds, and the pace is printed from a flag beside every figure in ticks because a figure in ticks with no pace beside it is not a figure.

∑ Math Interlude — a reflection in ticks, at ten a second

A tick is a hundred milliseconds, so a duration in milliseconds divided by a hundred is a duration in ticks. The first call took 2,138 milliseconds: 21.4 ticks. Embedding the two questions took 424 and 25, which is 4.2 ticks and 0.3, and the difference between the two is a load: the embedding model was not resident when the first question was sent and was when the second was. The two statements took 2,727 and 1,727 milliseconds, 27.3 ticks and 17.3, and the difference between those is the same load the other way, the small model coming back into memory for the first of them. Embedding the one accepted thought took 435, another load, 4.3 ticks. Add the six: 2,138 plus 424 plus 25 plus 2,727 plus 1,727 plus 435 is 7,476 milliseconds, and the bench prints 7.476 seconds, which is 74.8 ticks.

A village of twenty-four, one worker, one reflection each, is 24 times 7,476 milliseconds: 179,424 milliseconds, which is 2 minutes and 59.4 seconds, or 1,794 ticks. The cadence is 2,000 ticks. 1,794 over 2,000 is 0.897, so the one worker spends 90 per cent of every day making thoughts, and has the other 10 per cent, 206 ticks, for everything else a village asks a model for. That is with two questions a reflection. A third question adds, at the least, one more statement call and one more embedding with the model already resident, which the run prices at 1,727 and 25 milliseconds: 7,476 plus 1,752 is 9,228 milliseconds a reflection, and 24 of those is 221,472 milliseconds, or 2,215 ticks, which is more than the day, and a cadence that cannot be met is not a cadence.

The same in symbols, with the first chapter's:

T = 1 ÷ f

reflection = Rq + Re1 + Re2 + Rs1 + Rs2 + Rt

village = n × reflection

share = (village ÷ T) ÷ every

fticks a second the world is run at; 10 for every figure on this page
Thow long one tick is; 100 milliseconds here
Rqthe questions call, start to finish; 2,138 milliseconds above
Re1, Re2one embedding a question; 424 and 25 milliseconds, the first with a load in it
Rs1, Rs2one statement call a question; 2,727 and 1,727 milliseconds, the first with a load in it
Rtone embedding an accepted thought; 435 milliseconds, with a load in it
npeople in the village, each getting one reflection, with one worker asking; 24 above
everythe cadence in ticks, from the file; 2,000 here
a ÷ ba divided by b

Three things in that arithmetic decide the design and none of them is a matter of taste. The first call is the dearest single call and it is dear because of what goes in, not what comes out: 575 tokens of prompt and 29 of answer, and the prompt is the day. A window of forty-eight rows would be a first call about twice as long. The second is the load. Three times inside one reflection the resident model changes, and each change is a few hundred milliseconds paid to the disk rather than to the model; that is why the driver is four phases and not two loops, so that the embeddings are done together and the statements together, and a reflection changes model three times and not once a call. The third is that 90 per cent is not a budget anybody would choose, and the cadence is a day because a day is the shortest cadence this village can afford at this size on this machine, with two questions, and not because a person thinks once a day.

The recording itself is made by the same bench, from the same live models, and the run that makes it is the run that checks it: it writes the file under this chapter's name and holds it against the copy the build carries.

▣ Build · stage 12 — the recording, made again and held against the committed copy
$ podman exec -w /bench world-go go run ./cmd/reflect -mode record | tail -19
  Halla  step 1 at tick  2900: 3 calls, 3 embeddings, 1 thoughts written, 1 statements refused
  Halla  step 2 at tick  4900: 3 calls, 4 embeddings, 2 thoughts written, 0 statements refused
  Halla  step 3 at tick  6900: 3 calls, 4 embeddings, 2 thoughts written, 0 statements refused
  Halla  step 4 at tick  8900: 3 calls, 3 embeddings, 1 thoughts written, 1 statements refused
  Halla  step 5 at tick 10900: 3 calls, 4 embeddings, 2 thoughts written, 0 statements refused
  Ander  step 1 at tick  2900: 3 calls, 3 embeddings, 1 thoughts written, 1 statements refused
  Mose   step 1 at tick  2900: 3 calls, 3 embeddings, 1 thoughts written, 1 statements refused

  21 calls and 24 embeddings; every row a model answered was 768 numbers wide
  written to reflect94-reflections.json, 213175 bytes, 21 proposals

  against the copy this build carries
    exemplars/reflections.json       7dc66956619f1e8f    213175 bytes
    reflect94-reflections.json       7dc66956619f1e8f    213175 bytes
    21 proposals committed, 21 made here; of the first 21, 21 prompts are the same bytes
    and 21 answers are the same words, which is a fact about this machine and not a check

  1m2.885s inside the models, over 21 calls and 24 embeddings (measured here; yours will differ)
  1m2.98s for the whole recording (measured here; yours will differ)

Seven scenarios, each on a stream of its own that starts as the committed day: five steps of Halla's, one day apart, and one step each for Ander and Mose, all with the cap off so that the chain runs on to where the worked failure needs it. Twenty-one generations and twenty-four embeddings, 213,175 bytes on disk, most of it vectors, none of which is printed. Every prompt came out the same bytes as the committed copy, which is the check; every answer came out the same words as well, which is what a seeded answer at temperature nought does on one machine and is not something the file promises. The refusals in the seven lines are the seam's, live: two duplicate citations and two answers cut off at the limit, none of them written, every one of them in the recording so that the replay refuses them for the same reasons.

The chain cut after its third day

A thought is retrievable like any other row, so the next day's reflection can bring yesterday's thought to mind and draw on it, and the day after can draw on that. Nothing in the check above stops a thought being drawn only from thoughts, and the depth cap is what does. The chain mode replays Halla's five recorded days in order through the seam with the cap the file sets, and prints for each statement how many of the window, the eight retrieved and the citations were the world's rows and how many were thoughts.

▣ Build · stage 13 — the chain with the cap on
$ go run ./cmd/reflect -mode chain | tail -12
  step   tick  the window     retrieved     cited        depth  the seam
     1   2900  24 world 0 th  8 world 0 th  8 world 0 th      1  accepted, at depth 1
     1   2900  24 world 0 th  8 world 0 th  9 world 0 th      -  refused by cites: memory 8 is cited twice
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
     3   6900  21 world 3 th  5 world 3 th  5 world 3 th      3  refused by depth: a thought at depth 3, and the cap is 2
     3   6900  21 world 3 th  5 world 3 th  4 world 3 th      3  refused by depth: a thought at depth 3, and the cap is 2

  the chain is cut after step 3. Nothing was written there, so the recording's
  step 4 is about a stream this run did not write: the questions prompt rendered here is 1230 bytes, sha256 5adb48311f18d2d9, and the recording's was 1251 bytes, sha256 87c9fae77349bc2a: the recording is not a recording of this code
  the recording goes on for 2 step(s) with the cap off, and none of them replays here
  3 steps replayed, 3 thoughts written to Halla's stream, 2 refused for depth

Day one is the reflection above: one thought at depth 1, one statement refused for a duplicate. Day two, at tick 4900, the window is twenty-three of the world's rows and the thought, both statements cite the thought among seven world rows, and both are accepted at depth 2, which is the cap. Day three the window holds three thoughts, five of the eight retrieved for each question are the world's and three are thoughts, every statement cites all three, and a thought drawn from a depth-2 thought stands at depth 3, so both are refused and nothing is written. Then the replay stops, and the reason is the same reason the recording is trustworthy: day four's prompt, rendered from a stream with three thoughts in it, is not the prompt that was recorded from a stream with five, and a recording whose bytes do not match is not replayed past the mismatch. The seam has cut the chain at the depth the file says, and the last two days of the recording are days this stream did not have.

⚠ Worked failure — the cap off, and a thought about a thought about a thought

Here is the counterfactual, run: the same recording, the same seam, with the depth cap switched off by a flag the bench prints. This is the run the recording was made for.

$ go run ./cmd/reflect -mode chain -uncapped | tail -23
  step   tick  the window     retrieved     cited        depth  the seam
     1   2900  24 world 0 th  8 world 0 th  8 world 0 th      1  accepted, at depth 1
       1.1  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     1   2900  24 world 0 th  8 world 0 th  9 world 0 th      -  refused by cites: memory 8 is cited twice
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
       2.1  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
       2.2  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     3   6900  21 world 3 th  5 world 3 th  5 world 3 th      3  accepted, at depth 3
       3.1  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     3   6900  21 world 3 th  5 world 3 th  4 world 3 th      3  accepted, at depth 3
       3.2  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     4   8900  19 world 5 th  3 world 5 th  3 world 5 th      4  accepted, at depth 4
       4.1  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     4   8900  19 world 5 th  3 world 5 th  3 world 4 th      -  refused by cites: memory 8 is cited twice
     5  10900  18 world 6 th  2 world 6 th  2 world 5 th      5  accepted, at depth 5
       5.1  about 1 Halla, 2 Ander: Ander took 40 parcels out of Halla's hands, 20.0 grams.
     5  10900  18 world 6 th  2 world 6 th  2 world 6 th      5  accepted, at depth 5
       5.2  about 1 Halla, 2 Ander: Halla remembers that Ander took 40 parcels out of Halla's hands, 20.0 grams.

  5 steps replayed, 8 thoughts written to Halla's stream, 0 refused for depth,
  7 of the thoughts written drawn from an earlier thought
  the statements are a model's and yours will differ; every other column is the engine's

Start from the symptom, which is in the statements and is labelled a model's: eight thoughts over five days and seven of them are the same sentence, the eighth is the same sentence with Halla remembers that in front of it, and the sentence is row 11 of the stream copied out. Nothing was invented. That is not what the design expected a chain to do, and the page says so: the expectation was a thought that walked away from the world into things that never happened, and what this model did on this stream was walk away from the day into one line about one event. Read the numbers, which are the engine's, to see how. On day one the window in front of the model is the day, all twenty-four rows the world wrote. By day five it is eighteen of them and six copies of one line, because a thought weighs 4 and the meals weigh 3 and the questions the model asks are about Ander. Of the eight rows that come to mind for a question on day five, two are the world's and six are thoughts. Of the citations, five and six of them are thoughts. The depth column counts the generations: 1, 2, 3, 4, 5, each thought drawn from the one before it and standing one further from anything that happened.

The cause is the loop in the figure and the arithmetic of the last chapter. A thought is written into the same stream it was drawn from, at a weight above most of the day, with a text that a question about the same subject matches at least as well as the memory it copied, so it is retrieved above the row it came from; the model, shown the copy and the original, copies again; and every day adds two more copies to the window and pushes two more of the world's rows out of it. The day's other rows are not gone from the stream. They are gone from the eight that come to mind, and what does not come to mind cannot be drawn on. Recency does nothing to stop it, because at a half-life of 250 ticks everything older than a day is at nought, the day's rows and the thoughts alike, and the sum is decided by weight and cosine, both of which favour the copies.

Day four's second statement is the other thing to read. Asked How many parcels did Ander take out of Halla's hands?, a question the model wrote, the model answered Ander took out 40 parcels each time, with a total of 17 times, and seventeen is a number that appears nowhere in the stream. It was refused, and it was refused for citing row 8 twice, not for the seventeen. The seam reads identities and reads no numbers out of text, and had the model cited row 8 once, a thought with an invented count in its text would be in the stream with a weight of 4, about Halla and Ander, drawn from memories that check. The columns would all be true. The text would not, and nothing in this world reads a fact out of that column, which is the only reason it is safe to hold.

The fix is the cap, and the cap is not a fix for the copying: with the cap at 2 the model copies exactly as it did on days one and two, and the difference is that on day three the copies of copies are refused and the villager writes nothing. What the cap buys is a bound on how far a row in the table can stand from something that happened. A thought at depth 2 is one thought away from a memory the world wrote. A thought at depth 5 is five thoughts away, and on this recording each of those thoughts is a copy, so the distance is a distance of nothing, but the seam does not know that and could not check it if it did, and a model that did not copy would have put five generations of drift in the same five rows. Two is a number in a file. The flag comes off and the chain goes back to three steps.

Why citation checks read engine memory

Take the village away and the design is a rule about what a model's answer may be checked against. A prompt can ask for anything, and a model will produce something in the shape of it; what makes the answer safe to keep is not the asking but the existence, somewhere the model cannot reach, of a value the answer can be held against. Here that value is a list of identities the villager's own table holds, with a tick on each. The statement names identities; every identity either is in the list at a tick no later than now or is not; and the decision is arithmetic over stored numbers, so it is the same on every machine and can be replayed from a recording. The words the statement is made of are not held against anything, because there is nothing to hold them against, and the design does not pretend otherwise: it puts them in the one column nothing reads.

The second thing that generalizes is the refusal. A failed statement is refused whole, never trimmed, never repaired and never asked again with a correction, and the reason is not strictness for its own sake. A repaired answer is an answer partly written by the repairer, and the repairer is the engine, which was not supposed to write any of it; a trimmed answer keeps the words and drops the sources the words rested on; a correction turn is a second call at the price of the first, and the price section says what one call costs. Refusing costs one answer and the villager keeps its day. Every one of those alternatives looks kinder to the model and every one of them moves a decision out of a pure function and into somebody's judgement about how much of a wrong answer to keep.

The third is the loop. Any system that feeds a model's output back into the model's input has a chain in it, and a chain has a depth, and the depth will grow unless something bounds it, because nothing in the model has any way of knowing that the line in front of it is its own. The bound here is an integer on the row's provenance, computed from a column that only that kind of row may fill, enforced at the one place the row is written. It is cheap because the provenance was designed in two chapters ago, when the schema tied drawn_from to thought with an equality of two booleans; a stream that had not recorded what a thought was drawn from would have no depth to cap and no way to add one without an update on a table that takes none.

Checkpoint

✓ Checkpoint — two calls, four reasons, one column
  • Name the four reasons Cite refuses a statement and say, for each, which of the three things the check is handed decides it; then say why a memory that is Mose's and a memory that is nobody's are refused with the same sentence.
  • Given a statement that cites five memories, four of which check, say what the seam writes, and say what would be wrong with a row drawn from the four.
  • List the columns of row 89 and say for each whether the engine or the model decided it, and where the about column came from if not from the text.
  • From the six durations in the live run, work out a reflection in ticks at ten a second and a village of twenty-four's share of a 2,000-tick cadence, and say which three of the six have a model load inside them and how the run shows it.
  • Say what depth a thought drawn from two world rows and one depth-2 thought stands at, what the cap of 2 does with it, and what the chain run showed the copies doing to the window by day five with the cap off.
  • Say what an answer stopped at the limit looks like, which function refuses it, and why a field the shape does not have is refused and not skipped.
⚡ Exercises — try first, then reveal
Exercise 1 — a thought about nobody. Replay Mose's reflection from the recording and read the row it writes. Before you run it, say from the template alone what the about column of a thought drawn only from meals and walks must be.

The row is about nobody, and its text is nonsense:

$ go run ./cmd/reflect -mode reflect -who Mose | tail -7 | head -4
    | I remember eating 29 grams on day 6, 3. 2 grams on day 6, 7. 5 grams on day 7. 6, 22. 5 grams on day 8.
  drawn from, as the model gave them   81, 79, 68, 24, 77, 10, 75, 61
  the seam                             accepted, at depth 1
  the row appended                     id 89, tick 2900, thought, weight 6 (4 base + 2 first), about nobody, drawn from 10, 24, 61, 68, 75, 77, 79, 81

Eight citations, every one a meal or a walk of Mose's, every one at a tick no later than 2900, none twice: accepted. The about column is the union of eight empty about columns, which is nobody, and a thought about nobody is what a day of eating alone produces whatever its text says; no prompt rendered from a retrieval that brings this row to mind will be allowed to name anybody on its account. The text has grams that were never eaten and days this world does not count in, and the seam accepted it, because the seam checks what the statement is drawn from and not what it says. That is the boundary the chapter draws and this row is the plainest picture of it.

Exercise 2 — the cap one deeper. The bench takes the cap as a flag. Run the chain with the cap at 3 and predict, from the capped run on the page, which day it is cut after and how many thoughts Halla has then.

One more day, two more thoughts:

$ go run ./cmd/reflect -mode chain -depth 3 | tail -14 | head -11
  step   tick  the window     retrieved     cited        depth  the seam
     1   2900  24 world 0 th  8 world 0 th  8 world 0 th      1  accepted, at depth 1
     1   2900  24 world 0 th  8 world 0 th  9 world 0 th      -  refused by cites: memory 8 is cited twice
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
     2   4900  23 world 1 th  7 world 1 th  7 world 1 th      2  accepted, at depth 2
     3   6900  21 world 3 th  5 world 3 th  5 world 3 th      3  accepted, at depth 3
     3   6900  21 world 3 th  5 world 3 th  4 world 3 th      3  accepted, at depth 3
     4   8900  19 world 5 th  3 world 5 th  3 world 5 th      4  refused by depth: a thought at depth 4, and the cap is 3
     4   8900  19 world 5 th  3 world 5 th  3 world 4 th      -  refused by cites: memory 8 is cited twice

  the chain is cut after step 4. Nothing was written there, so the recording's

Day three's two thoughts are accepted at depth 3, day four's first is refused at depth 4 and its second for the duplicate it had anyway, and the chain is cut after day four with five thoughts in Halla's stream instead of three. The header of the run says the cap came off the flag and not the file, so a reader of the listing cannot mistake it for the shipped setting. Every depth in the table is the same number it was under the cap of 2 and under no cap, because depth is a property of the row and the cap only decides where the seam stops accepting it.

Exercise 3 — what a thought weighs against the day it came from. Replay Halla's second day and read the ranking for its first question. Before you run it, work out from the last chapter's decay what recency every row of the day has at tick 4900, and where a thought at tick 2900 weighing 6 lands against row 11.

The day is at nought, the thought is nearly at nought, and row 11 still comes first:

$ go run ./cmd/reflect -mode reflect -step 2 | tail -48 | head -5
  question 1, embedded, and Halla's 25 rows at ticks up to 4900 ranked against it
  rank   id   tick weight  recency import   relev   score  kind     text
     1   11   1015     10   0.0000   1.00  0.4090  2.2272  saw      Ander took 40 parcels out of Ha...
     2    8    999      7   0.0000   0.70  0.3747  1.8242  did      handed Ander 50 parcels, 25.0 g...
     3   89   2900      6   0.0039   0.60  0.3997  1.8029  thought  Ander took 40 parcels out of Ha...

At tick 4900 row 11 is 3,885 ticks old, which is fifteen and a half half-lives, and a half halved fifteen times is below a hundred-thousandth; the column prints 0.0000 for every row of the day. The thought is 2,000 ticks old, eight half-lives, 0.0039. So the ranking is weight and cosine alone, and the thought's 0.60 puts it third behind the two heaviest rows about Ander. It is third with a cosine below the row it copied, 0.3997 against 0.4090, because a full stop was added; by day three it is one of three copies and the copies outnumber the originals in the eight. The second-day questions the model asked were What did you do today? and How was your day?, which are questions about a day on which, in the stream, nothing happened; the rows are the recording's and yours will differ.

So Halla has a thought at the end of her day, and it is row 89: a copy of the worst thing that happened to her, weighed 6, about the two people it concerns, drawn from eight memories the engine checked one at a time, and it cost seventy-five ticks of a world that runs ten to the second. The words in it are the model's and nothing reads them. What reads the row is the next question put to her stream, which will bring it to mind on its weight and its cosine like any row the world wrote, and a plan is a question of that kind with an answer that has to be an action.