The World Vol 7 · The Living Valley
ch 79 / 105
Chapter 79

The Founding Document

The world names table

Every animal the archive ever wrote a line about is a row in the creature table now, and not one of them has a name. That is right and it stays right: an animal here is a number, handed out in birth order, and the archive that recorded it never had a word to say about anything else.

A world's names are data, written once, at tick zero, into rows nothing ever rewrites. The file they came out of is read exactly one time; from the second tick onward the world asks the database what it is called, and what the file says afterwards is of interest to no caller.

The trouble is that the ground those animals walked on has no name either, and neither has the world they walked on it in. Two of the seven tables hold history. The world table is empty, the chronicle is empty, and a database that can climb a pedigree to the founder at the top of it cannot say what any of this is called.

Every page of this volume has been about mechanism: a seventh action, a second row of animals, a schema, a save that is whole or absent. None of it needed a word. A valley works perfectly well as terra.Valley, and an operator running one on their own machine can go a long time without minding.

What breaks is the first moment somebody else reads the history, or the first moment you read it yourself after a year away, and the record says two hundred and thirty-six animals standing where it could have said where.

There are two histories in this database and they want separating before either gets a name in it. The archive is a record of animals: one line a birth, one a death, written by the pool as the valley ran, machine-made and complete and never consulted by anything inside a tick.

The chronicle is a record of a world. Its rows are things somebody chose to write down, in sentences, because somebody is meant to read them. It has the same append-only discipline the archive has, and it has held nought rows since the migration that made it, because until a world has a name there is nothing to say about it that a reader would understand.

The names have to be written at tick zero. Then every row the world ever holds was written under one vocabulary. Write them in year forty and there are two vocabularies in one table, with nothing in a row to say which of them it belongs to, and no query anybody can write that finds the whole of the history by name.

By the end of this page there is a founding document in configs/genesis.json: eleven keys, eleven names and about three hundred and sixty words of the operator's own note. It is loaded once, its lines become the first eleven rows the chronicle holds, and the world row carries the whole of it as one blob. Then a valley runs for eighty years writing a line of history a year, somebody renames its ground in the fortieth, and the history comes apart quietly enough that nothing anywhere complains about it.

Two things are deliberately not on this page. Nothing here runs unattended: every command below is one somebody typed and watched. And nothing in a tick reads any of it, which is the same rule that has held since the database arrived. The five simulation packages do not know that internal/store exists, no creature's behaviour depends on a row anybody wrote, and a valley that has been told what its ground is called behaves exactly like one that has not.

The founding questions

The founding document is a file of answers. The questions live in the program, as a list with an order on it, and that split is the whole design and not a detail of it.

▣ Build · stage 1 — the questions
// internal/store/genesis.go
// Key is one line of the founding document: the key it is written
// under, the question it asks the operator, and whether the name it
// fixes is a thing you can have two of.
//
// Counts is not decoration. A program that has one form of a name and
// needs two makes the second one up, and a program that makes a plural
// up by adding a letter is a program that one day prints Firstlights.
// A name a world is going to print in both forms is written down in
// both forms, by the person who chose it.
type Key struct {
	Name   string
	Asks   string
	Counts bool
}

// Keys is the founding document's schema and its order.
//
// It lives here and not in the file, which is the whole of what makes
// this a document and not a bag of settings. A JSON object has no
// order; this list has one, and it is the order the lines go into the
// chronicle in. A key in the file that is not on this list is refused,
// and so is a key on this list that the file has not answered: an
// operator who skipped one did not decide to leave it out, they got
// as far as ten and were called away.
var Keys = []Key{
	{"world", "what the whole of it is called", false},
	{"ground", "the land it runs on", false},
	{"living", "what walks about on the ground", true},
	{"keeper", "who watches it", false},
	{"people", "where people gather", false},
	{"arrival", "how somebody gets in", false},
	{"way", "the doors from one piece of ground to the next", true},
	{"trade", "what changes hands", false},
	{"edge", "what is past the mapped ground", false},
	{"waste", "where what is finished with goes", false},
	{"sought", "the thing worth going after", false},
}

Eleven, and the count is a decision somebody has to make once. Fewer and the document stops being a founding and becomes a configuration file with a valley name in it. Many more and it stops being something an operator sits down and writes in an evening, which is the only kind of document that gets written honestly. The test for a key is whether a person who has never seen this world would need it to say one sentence about the place, and every one of the eleven passes that test today.

Counts is the field on that list most likely to be deleted by somebody tidying up, so here is what it does. Two of the eleven names get printed in both forms: the animals, and the doors. English makes plurals with a letter about ninety percent of the time, which is exactly the hit rate that gets a rule like that written into a program and then never revisited. The tenth case is the one on the front page. A name is a thing somebody chose, and the person who chose it is the person who knows what two of them are called, so the file asks and the loader refuses a countable name that came with one form.

One field in the file is not a key and not a name. opened is the tick the note is dated to, it is nought in every document anybody should ever write, and it goes straight into the opened column of the world row, where the schema checks it is a real tick and nothing else ever touches it. It could have been supplied by the loader, since the loader knows perfectly well what tick a founding happens on. It is in the file because a date somebody wrote is a different object from a date a program filled in: the first one can be read back in five years and believed, and the second one is only ever as true as the code that was running that afternoon.

Now the answers. What follows is one filled-in document, and it is an exemplar in the strict sense this book uses the word: yours will differ, and it is supposed to. Nothing below is canon, nothing below is checked by anything, and no part of this world reads a single one of these words for meaning. The names in it are the ones this book's world happens to use. The names in yours are yours.

▣ Build · stage 2 — the document, filled in once
{
  "opened": 0,
  "entries": {
    "world": {
      "one": "The World",
      "note": "This is The World. I built it and I run it, and on the tick this is dated to there is nothing in it but ground. I am writing the names down now, before anything happens that could disagree with them. I read this file once and then I leave it alone. Anything I want to say afterwards goes in the chronicle with a tick on it."
    },
    "ground": {
      "one": "The Hollow",
      "note": "The land is The Hollow. It is one valley with stone round the rim and soil in the bottom, and everything alive here is alive in it. It is the ground I have made, and not all the ground there is."
    },
    "living": {
      "one": "hobb",
      "many": "hobbs",
      "note": "The animals are hobbs. One of them is a hobb. They eat, walk, breed and die, and not one of them is named: they are numbered, and the numbers are in the archive."
    },
    "keeper": {
      "one": "Asteria",
      "note": "Asteria keeps it. She is not down in the valley and she does not walk about in it; she reads what the world writes down. That is the whole of what I have settled about her, and it is enough to be going on with."
    },
    "people": {
      "one": "Firstlight",
      "note": "People gather at Firstlight. Nobody is there and nothing stands there. The name is here at tick zero because this is the last moment a name can be put down and count as founding."
    },
    "arrival": {
      "one": "The Commons",
      "note": "The way in is The Commons. Anybody who comes here comes through it, and there is no second door."
    },
    "way": {
      "one": "Waygate",
      "many": "Waygates",
      "note": "The doors between one part of the ground and another are Waygates. One of them is a Waygate."
    },
    "trade": {
      "one": "grain",
      "note": "What changes hands is grain. Nothing here trades and nothing here is priced. I am writing the word down anyway, because it costs me one line today and cannot be had at all tomorrow."
    },
    "edge": {
      "one": "the Unwritten Places",
      "note": "Past the mapped ground are the Unwritten Places. It is the word for everywhere I have not built."
    },
    "waste": {
      "one": "the Midden",
      "note": "What is finished with goes to the Midden. Bodies, husks, whatever falls: it ends in one place and the ground takes it back."
    },
    "sought": {
      "one": "the Wellspring",
      "note": "The thing worth going after is the Wellspring. I know what it is called. I have settled nothing else about it and I am not going to today."
    }
  }
}

Read one of those notes and then read it again asking what kind of writing it is, because that question is the hardest part of this whole chapter and the part a reader is most likely to get wrong on their own file. It is a note. Somebody has built a thing, they are about to leave it running, and they are putting down what they made while they still remember making it. Present tense, plain sentences, first person, and the flat willingness to write I have settled nothing else about it where there is nothing else settled.

Three other registers are sitting right there and every one of them will produce something that reads better and works worse. There is the scriptural one, where the ground is spoken of as having been given rather than made. There is the mythic one, with an age before the valley and a reason the animals came. And there is the design document, with its bullet points about what the mechanism is for. All three describe a world from outside it. The note describes a world from the desk of the person who built it, which is the only vantage point that is actually true on the tick this file is dated to, and the only one that stays true when somebody reads it back in year forty and finds it has not aged into a promise.

It is easier to see on one key than in the abstract, so here is the edge of the map written three ways. The mythic one: Beyond the mapped ground lie the Unwritten Places, where no foot has fallen and no name has yet been given, and they wait. The design one: edge: the label for regions outside the generated grid; printed wherever a position falls off the valley. And the one in the file: Past the mapped ground are the Unwritten Places. It is the word for everywhere I have not built.

The first knows things the operator does not know. Nobody has established that no foot has fallen anywhere, and they wait is a promise dressed as a description; a reader who takes it seriously has been told something about this world that is not true of it. The second is true of the program on the afternoon it was written and stops being true the first time the program changes, which makes it the one register that actively rots. The third says who wrote it, says what the word means, and says the limit of what has been decided. It is the only one of the three that will still be accurate in year four hundred, because it is a statement about a decision and not about a world or about a code path.

The eight keys no code has any use for yet are the argument this whole page rests on, so the page is blunt about them. Three of the eleven names go on a line this world already prints: what the world is called, what the ground is called, what the animals are called. The other eight name things this valley does not have. There is no settlement in it, nothing arrives, nothing trades, nothing is sought, and the only place anything is finished with is a pile of litter with a number on it. Every one of those eight is written anyway, at tick zero, in one sitting, by somebody who does not know which of them is going to matter.

The evolution volume made this argument about three genes and it is the same argument made about prose. Aggression, wariness and tameness were inherited and crossed and archived through a whole volume in which nothing read any of them, and the case for keeping them was never that something might read them one day. It was that a run which has finished cannot be given a gene. The genomes exist in exactly one file, the animals are dead, and the only way to get a number that is not in that file is to run the two centuries again, which produces a different two centuries.

A founding document is that argument with the numbers taken out. It has exactly one moment when it can be written, and the moment is over by the second tick. Afterwards you can add a key to the file any afternoon you like and it changes nothing, because the file is not what anything reads. You can add a row to the chronicle any afternoon you like, and it will carry the tick you added it on, which is the correct and honest behaviour of an append-only history and is also the reason it cannot be a founding. Eleven lines of prose written before anything happened cost an evening. The same eleven lines written in year forty are eleven lines about year forty.

▣ Build · stage 3 — the document, checked against the questions
$ go run ./cmd/genesis -mode draft
genesis: a founding document, before there is anything to found

  genesis.json, and the eleven questions it answers

   key      asks for                                         the name it fixes      words  printed
   world    what the whole of it is called                   The World                 67  yes
   ground   the land it runs on                              The Hollow                41  yes
   living   what walks about on the ground                   hobb / hobbs              33  yes
   keeper   who watches it                                   Asteria                   45  no
   people   where people gather                              Firstlight                34  no
   arrival  how somebody gets in                             The Commons               19  no
   way      the doors from one piece of ground to the next   Waygate / Waygates        18  no
   trade    what changes hands                               grain                     34  no
   edge     what is past the mapped ground                   the Unwritten Places      18  no
   waste    where what is finished with goes                 the Midden                23  no
   sought   the thing worth going after                      the Wellspring            28  no
                                                                                      360

  11 keys, 360 words of the operator's own note, 2279 bytes of jsonb
  3 of the names go on a line this world already prints; the other 8 it has no use for
  dated to tick 0, which is the only tick a founding document is ever dated to

  and the four ways a document is refused
    a question left unanswered
      the founding document has no "sought": the thing worth going after
    an answer nobody asked for
      the founding document answers weather, which this world does not ask
    a countable name written once
      "living" names one hobb and no more: a name this world counts is written twice
    a plural on a name nothing counts
      "ground" names "The Hollow" and "The Hollows", and this world never counts them

No database anywhere near that run. The document is a file and the checks are arithmetic over it, so the first thing anybody does with a draft is the thing that needs nothing running. Three hundred and sixty words is the size to aim at. Under three hundred and the notes stop being notes and become labels; over five hundred and you are writing the myth.

The four refusals at the bottom are the loader run against a spoiled copy of the document, one thing wrong at a time. The two in the middle are a matched pair and the reason for both is the same. A missing answer is refused because a world founded on a blank prints a blank for the rest of its life, and the day somebody notices is a day far too late to fix it. An extra answer is refused because a key nothing asks for is a name nothing will ever read, and a file that accepts those silently is a file that accumulates them: three years on it holds a dozen words no row can account for, and no way to tell which of them the world is actually using.

DisallowUnknownFields on the decoder does half of that for free, at the level of the document's own shape, and the loop over Keys does the other half at the level of its contents. Both matter. The first catches a misspelt entries; the second catches a misspelt ground.

▣ Build · stage 4 — the same checks, as tests
$ go test ./internal/store/ -run 'EveryQuestionTheDocumentAsksIsAnswered|AQuestionLeftUnansweredIsRefused|AnAnswerToAQuestionNobodyAskedIsRefused|ANameThisWorldCountsIsWrittenDownTwice|APluralNobodyAskedForIsRefused|TheLinesComeOutInTheDocumentsOwnOrder|TheDocumentComesBackOutOfTheBlobUnchanged' -v
=== RUN   TestEveryQuestionTheDocumentAsksIsAnswered
    genesis_test.go:55: 11 keys, 360 words of the operator's own note, and a name on every one of them
--- PASS: TestEveryQuestionTheDocumentAsksIsAnswered (0.00s)
=== RUN   TestAQuestionLeftUnansweredIsRefused
    genesis_test.go:65: store: the founding document has no "sought": the thing worth going after
--- PASS: TestAQuestionLeftUnansweredIsRefused (0.00s)
=== RUN   TestAnAnswerToAQuestionNobodyAskedIsRefused
    genesis_test.go:76: store: the founding document answers weather, which this world does not ask
--- PASS: TestAnAnswerToAQuestionNobodyAskedIsRefused (0.00s)
=== RUN   TestANameThisWorldCountsIsWrittenDownTwice
    genesis_test.go:88: store: "living" names one hobb and no more: a name this world counts is written twice
--- PASS: TestANameThisWorldCountsIsWrittenDownTwice (0.00s)
=== RUN   TestTheLinesComeOutInTheDocumentsOwnOrder
    genesis_test.go:106: 11 lines, all of them tick 0 and kind "genesis", in the order Keys names and no other
--- PASS: TestTheLinesComeOutInTheDocumentsOwnOrder (0.00s)
=== RUN   TestTheDocumentComesBackOutOfTheBlobUnchanged
    genesis_test.go:129: 2279 bytes of jsonb, and the same eleven answers back out of it
--- PASS: TestTheDocumentComesBackOutOfTheBlobUnchanged (0.00s)
=== RUN   TestAPluralNobodyAskedForIsRefused
    genesis_test.go:143: store: "ground" names "The Hollow" and "The Hollows", and this world never counts them
--- PASS: TestAPluralNobodyAskedForIsRefused (0.00s)
PASS
ok  	theworld/internal/store	0.006s

Seven tests on the document this build ships, and not one of them opens a socket. The fifth is the one that would be easy to leave out and is the reason the order lives in Keys: eleven lines come out in the order the list names, all of them at tick nought, and a Go map iterating in whatever order it feels like on the afternoon has no say in it. The sixth is the round trip. The document goes into the world row as two thousand two hundred and seventy-nine bytes of jsonb and comes back out as the same eleven answers, which is the only reason it is safe to stop reading the file.

Writing the document and names

The document has two places to go and they are two different kinds of thing. The world row wants it whole, as a blob, for the same reason the creature table keeps a genome whole: nothing will ever filter on a line of prose, and the only question anybody asks it is what is this called. The chronicle wants it as rows, because the chronicle is what happened, and a world being named is a thing that happened.

▣ Build · stage 5 — the founding
// internal/store/genesis.go
// Genesis founds a world: the one world row, with the document in it,
// and the document's lines as the first rows the chronicle holds, all
// of it in one transaction.
//
// One transaction, for the same reason a snapshot is one. A world
// whose row exists and whose chronicle is empty is a world that was
// named by a file nobody can now read back, and a chronicle with
// founding lines in it and no world above them is a history of
// nothing. Neither state has to be reasoned about, because neither can
// be reached.
//
// The document goes into the world row whole, as one blob, and the
// same prose goes into the chronicle as rows. That is not two copies
// of one thing kept in step: the blob is the vocabulary, read back by
// name whenever the world prints one, and the rows are history, read
// back in order beside everything else that ever happened. They are
// written together and neither is ever written again.
func (d *DB) Genesis(ctx context.Context, doc Doc, seed uint64) error {
	world := doc.One("world")
	if world == "" {
		return fmt.Errorf("store: founding a world the document does not name")
	}
	blob, err := json.Marshal(doc)
	if err != nil {
		return fmt.Errorf("store: founding %q: %w", world, err)
	}
	tx, err := d.pool.Begin(ctx)
	if err != nil {
		return fmt.Errorf("store: founding %q: %w", world, err)
	}
	defer tx.Rollback(ctx)

	const row = `INSERT INTO world (world, seed, opened, genesis) VALUES ($1, $2, $3, $4)`
	if _, err := tx.Exec(ctx, row, world, int64(seed), doc.Opened, blob); err != nil {
		return fmt.Errorf("store: founding %q: %w", world, err)
	}
	const line = `INSERT INTO chronicle (tick, kind, text) VALUES ($1, $2, $3)`
	for _, l := range doc.Lines() {
		if _, err := tx.Exec(ctx, line, l.Tick, l.Kind, l.Text); err != nil {
			return fmt.Errorf("store: founding %q: the %q line: %w", world, l.Kind, err)
		}
	}
	if err := tx.Commit(ctx); err != nil {
		return fmt.Errorf("store: founding %q: %w", world, err)
	}
	return nil
}

Twelve statements, one transaction, one connection out of a pool of four. The pool is small on purpose and this is the sort of caller it was sized for: something that runs between ticks, holds a connection for as long as a founding takes, and hands it back. Nothing inside a tick is anywhere near it.

The tick this happens on is a boundary in the same sense the snapshot page used the word: nothing is part-way through, no view is open, no creature is half-stepped, because no creature has taken a step. Tick zero is the easiest boundary this world will ever have and the founding is taken on it for the same reason a save is taken on one. There is one moment at which the whole of a world can be written down without anybody having to say what was in flight, and a founding gets the cleanest instance of it going.

Not one of the eleven chronicle rows says which key it came from. The chronicle's columns are a tick, a kind and some text, and all eleven founding rows carry the same kind, because what happened was one thing: a world was named. How the file that carried the words was laid out is a fact about the file, and it is in the world row, in the blob, where the names are. That division is the one to hold on to, and the rest of this page is a demonstration of what it buys.

The database has to be empty for this, so start it empty. The data directory is a tmpfs, which means a restart is a fresh server with no rows anywhere in it, and the last thing the archive page did was leave the creature table loaded.

$ podman kill world-db && podman start world-db
world-db
world-db
$ podman exec -w /bench world-go go run ./cmd/genesis -mode found
genesis: a world named, on the tick before it has any history

  the document was read from genesis.json and will not be read again

  before it
    world      0 rows
    chronicle  0 rows

  one transaction: the world row and every line of the document
    world      1 row
    chronicle  11 rows

  the world row, read back
    world      The World
    seed       5
    opened     tick 0
    genesis    11 keys, 360 words

  the chronicle, ORDER BY entry, cut to fit
    entry  tick  kind      text
        1     0  genesis   This is The World. I built it and I run it, and on the tick...
        2     0  genesis   The land is The Hollow. It is one valley with stone round t...
        3     0  genesis   The animals are hobbs. One of them is a hobb. They eat, wal...
        4     0  genesis   Asteria keeps it. She is not down in the valley and she doe...
        5     0  genesis   People gather at Firstlight. Nobody is there and nothing st...
        6     0  genesis   The way in is The Commons. Anybody who comes here comes thr...
        7     0  genesis   The doors between one part of the ground and another are Wa...
        8     0  genesis   What changes hands is grain. Nothing here trades and nothin...
        9     0  genesis   Past the mapped ground are the Unwritten Places. It is the ...
       10     0  genesis   What is finished with goes to the Midden. Bodies, husks, wh...
       11     0  genesis   The thing worth going after is the Wellspring. I know what ...

  11 rows, all of them tick 0, in the order the document names its keys

Eleven rows numbered one to eleven, every one of them on tick nought. The numbers came from the database and could not have come from anywhere else: entry is GENERATED ALWAYS AS IDENTITY, so an insert that tries to supply its own is refused outright and there is no road by which a program puts a row in the middle of a history. The schema page argued for that column against exactly this case, because eleven things landing on one tick is a tick where the tick is not a key, and here are the eleven.

Notice what the founding did to the rest of the schema without doing anything to it. The snapshot table has a foreign key into world, so until this ran there was nowhere for a save to hang. The world row exists now and it is the last time it will be written.

Which leaves the question of how a running world gets a name out of a blob. The names are inside the document, under a key, and Postgres can reach into jsonb without the value coming back to the client first.

$ podman exec world-db psql -U world -d world -c "SELECT k AS key, genesis -> 'entries' -> k ->> 'one' AS name FROM world, jsonb_object_keys(genesis -> 'entries') AS k ORDER BY key;"
   key   |         name         
---------+----------------------
 arrival | The Commons
 edge    | the Unwritten Places
 ground  | The Hollow
 keeper  | Asteria
 living  | hobb
 people  | Firstlight
 sought  | the Wellspring
 trade   | grain
 waste   | the Midden
 way     | Waygate
 world   | The World
(11 rows)

Eleven names out of one column, in alphabetical order because the query said so and for no other reason. That is the vocabulary as the database holds it, and any process that can reach this database can have it without owning a copy of the file. The two arrows are the two to learn if you have not met them: -> reaches into jsonb and gives back jsonb, so it can be chained, and ->> does the same and gives back text, so it goes at the end.

⌥ Tool — psql, and looking inside a blob

Three things make a jsonb column bearable from a terminal. \x turns on expanded output, which prints one column a line instead of one row a line and is the difference between reading a document and watching it wrap. jsonb_pretty(genesis) prints the whole blob indented. jsonb_object_keys is the set-returning function used above: it hands back one row a key, which is how a blob is walked without the program knowing what is in it. The operators, the functions and the containment tests this page does not use are all at postgresql.org/docs/16/functions-json.html.

Now the half of the rule that has not been shown yet: the file is finished with. The way to demonstrate that is to hand the program a file that disagrees with the database and watch it not care. The bench writes an edited copy of the document to a scratch file of its own, with one name changed, and reads that copy off disk the way any process started by an operator reads a config file.

▣ Build · stage 6 — a name asked of the database, not of the file
// internal/store/genesis.go
// Doc reads the founding document back out of the world row. It is
// the only place a running world gets a name from once it has been
// founded, and it is asked once, when the process starts.
func (d *DB) Doc(ctx context.Context) (Doc, error) {
	var doc Doc
	// One row by construction: the world table takes one and the
	// database refuses a second. The ORDER BY is there because every
	// query in this book carries one and a rule with an exception is
	// a rule somebody has to remember.
	const q = `SELECT genesis FROM world ORDER BY world`
	var raw []byte
	if err := d.pool.QueryRow(ctx, q).Scan(&raw); err != nil {
		return doc, fmt.Errorf("store: reading the founding document: %w", err)
	}
	return ReadDoc(raw)
}
$ podman exec -w /bench world-go go run ./cmd/genesis -mode names
genesis: a world started with a document that has been edited since

  the file this process was handed, an edited copy of the founding document
    file                   genesis-79.json
    the ground             the Hollow

  what the database holds
    world                  The World
    ground                 The Hollow
    living                 hobb / hobbs
    keeper                 Asteria
    people                 Firstlight
    arrival                The Commons
    way                    Waygate / Waygates
    trade                  grain
    edge                   the Unwritten Places
    waste                  the Midden
    sought                 the Wellspring

  and the three of them this world puts on a line
    The World, on the ground it calls The Hollow, with hobbs walking about on it

  the file says "the Hollow" and the world says "The Hollow"
  the document was read on tick 0 and the names have come out of the database since

The file on disk says one thing and the world says another, and the world is right. There is no cleverness in that and no reconciliation step: the program never asks the file, so there is nothing to reconcile. ReadDoc is the same function in both cases, which is the small piece of design that makes it work at all. The bytes that come out of the column go through exactly the checks the file went through, so a world row somebody has been at by hand is refused on the way in rather than trusted because it came from a database.

Where a running world gets a name The founding document as a file on the left, read once at tick zero into a single transaction in the middle. That transaction writes two things: the world row's genesis column, which holds the whole document as one blob, and eleven chronicle rows, all on tick zero, numbered one to eleven by the database. On the right, the running world at every tick after the first, which takes every name it prints from the blob in the world row. A dashed red path runs from the file along the bottom to the running world and is crossed out: after tick zero the file is never read again, so editing it changes nothing. WHERE A RUNNING WORLD GETS A NAME read once at tick 0, asked of the database ever after configs/ genesis.json 11 keys, 360 words read once, at tick 0 one transaction world.genesis the document, one blob chronicle 11 rows, all tick 0 entry 1 to 11 every name it prints the world, tick > 0 the file, after tick 0 never read again: editing it changes nothing
Figure 79.1 — one reading of one file, and two destinations for what came out of it. The blob is the vocabulary and is asked by name; the eleven rows are history and are read in order beside everything else that ever happened. The path along the bottom is the one that does not exist.

The renamed-file run

A chronicle with nothing but a founding in it proves nothing, so the world has to run. The bench founds a valley of the kind this volume has been running all along, three trophic levels on sixteen cells by twelve, and writes one line of history a year. The line is annalistic, which is the only register a chronicle entry has ever wanted: what year it is, what the count was, what changed since the last one.

▣ Build · stage 7 — a line of history a year
// internal/store/genesis.go
// Tell appends one line to the chronicle. It is called between ticks
// and never inside one, like everything else in this package.
func (d *DB) Tell(ctx context.Context, tick int, kind, text string) error {
	const q = `INSERT INTO chronicle (tick, kind, text) VALUES ($1, $2, $3)`
	if _, err := d.pool.Exec(ctx, q, tick, kind, text); err != nil {
		return fmt.Errorf("store: telling the chronicle about tick %d: %w", tick, err)
	}
	return nil
}
// cmd/genesis, once a year, between one tick and the next
text := fmt.Sprintf("year %d: %d %s standing in %s, %d born and %d gone",
	y, live, held.Many("living"), held.One("ground"), born, deaths)
if err := db.Tell(ctx, w.tick(), "count", text); err != nil {
	die(err)
}

Two names go into that sentence and both of them came out of held, which is the document as the database handed it back when the process started. That is the whole mechanism of this chapter arriving where it matters. The plural is the one the operator wrote down rather than one the program derived, the ground is the one the world row holds, and the text that lands in the chronicle is a finished English sentence with the names already in it.

The database has to be empty again, because this run founds its own world. Ten years first, with nothing renamed in them, so that what a chronicle looks like when no file has interfered with it is on the page before the interference.

$ podman kill world-db && podman start world-db
world-db
world-db
$ podman exec -w /bench world-go go run ./cmd/genesis -mode years -years 10 -at 0
genesis: 10 years of one valley, and nothing renamed in it

  founded at tick 0: The World, on ground it calls The Hollow, 29 hobbs standing
  11 lines of the founding document in the chronicle, every one of them tick 0

   year     tick  walking     born     gone  chronicle
      1     3150       11       45       63         12
      2     6750       23       84       90         13
      3    10350       25      122      126         14
      4    13950       19      195      205         15
      5    17550       43      252      238         16
      6    21150       12      338      355         17
      7    24750       28      385      386         18
      8    28350      105      628      552         19
      9    31950      171      869      727         20
     10    35550      191     1013      851         21

  the chronicle at the end of 10 years
    rows                                            21
    of those, the founding document                 11
    and the yearly count                            10

  asking the history about the ground it has been running on
    the name the world row holds           The Hollow
      rows carrying it                              11
      first on tick                                  0
      last  on tick                              35550

  21 rows, one name, and every row that names the ground at all carries it

  35550 ticks in 10.286s, 3456 ticks a second (measured here; yours will differ)

The chronicle column is the one to watch and it is the least dramatic thing in the block: eleven rows before the first tick, then twelve, then thirteen, one a year, for ever. Everything else in that table is the valley behaving the way the pyramid chapter said it does. Twenty-nine animals go in, the population falls to eleven in the first year and to twelve again in the sixth, and then it climbs to a hundred and ninety-one by the tenth. Founders are scattered on a valley they did not grow up in, most of them starve, and the ones that do not are the ancestors of everything afterwards. The timing on the last line, and the one on the eighty-year run further down, are an eight-core Ryzen 7 3700X talking to itself and will be different on your machine; every other number in either block is arithmetic and comes out the same anywhere.

The last block is the one to hold in mind for the next two pages. Eleven rows of this history name the ground: the ten yearly counts, and the founding note about the ground itself. The first is on tick nought and the last on tick 35,550, which is the whole of the history, because a question asked by name in a world with one vocabulary gets the whole of the history. That is the property about to be lost.

Now the mistake, on a longer run of the same valley.

⚠ Worked failure — the ground renamed in the fortieth year

Here is the change, and it is deliberately the smallest one available. The operator has been reading census lines for forty years, they read standing in The Hollow in the middle of a sentence, and the capital on the article has started to look wrong. So they open configs/genesis.json, take the capital off, and save. One letter. Nobody would call this a rename and it is one.

The first thing that happens is nothing, and that is the design working: the file is not read, so the world goes on printing what the world row says. To make the change take effect at all somebody has to found the world again with the edited document, and the run below does that in its fortieth year, in the middle of the eighty it covers.

$ podman kill world-db && podman start world-db
world-db
world-db
$ podman exec -w /bench world-go go run ./cmd/genesis -mode years
genesis: 80 years of one valley, and a rename in year 40

  founded at tick 0: The World, on ground it calls The Hollow, 29 hobbs standing
  11 lines of the founding document in the chronicle, every one of them tick 0

   year     tick  walking     born     gone  chronicle
     10    35550      191     1013      851         21
     20    71550      236     1643     1436         31
     30   107550      238     1731     1522         41
     40   143550      235     1773     1567         51

  the ground renamed, 143550 ticks in
    the file on disk now names it             the Hollow
    the world row still names it              The Hollow
    founding again, with the edited document
      SQLSTATE  23505
      said      duplicate key value violates unique constraint "world_key"
      detail    Key (world)=(The World) already exists.
    and the update, which nothing in this world calls
      the world row now names it              the Hollow

     50   179550      237     1843     1635         61
     60   215550      236     1902     1695         71
     70   251550      237     1931     1723         81
     80   287550      238     1965     1756         91

  the chronicle at the end of 80 years
    rows                                            91
    of those, the founding document                 11
    and the yearly count                            80

  asking the history about the ground it has been running on
    the name the world row holds now       the Hollow
      rows carrying it                              40
      first on tick                             147150
      last  on tick                             287550
    the name it was founded under          The Hollow
      rows carrying it                              41
      first on tick                                  0
      last  on tick                             143550
    either name, with case set aside       the Hollow
      rows carrying it                              81
      first on tick                                  0
      last  on tick                             287550

  91 rows, two names, and not one row that says which of them it was written under

  287550 ticks in 2m14.826s, 2133 ticks a second (measured here; yours will differ)

Take the middle of that block first. The second founding is refused, and 23505 is SQL's code for a unique constraint that has been broken. The constraint named is world_key, the primary key on the world's own name, and the detail line says which value was already there. That is the database doing the job the schema page gave it. Had the operator renamed the world as well as the ground there would have been a second refusal waiting behind the first: world_is_one_row, the unique index on a boolean column that is always true, which is three lines of schema meaning this table holds one row and never two.

So the refusal holds, and the operator goes round it, because a refusal that can be gone round is not a rule. One UPDATE, no constraint anywhere in the way, and the world row names the ground the new way from that tick on. Nothing in this world calls that function; it is in the package so that what it costs can be run instead of described.

Ninety-one rows at the end, and every single one of them is correct. Every count was right when it was written, every tick is the tick it happened on, no row has been touched since it landed, and the append-only history has done precisely what an append-only history is for. The damage sits between the rows and not in any of them, and it shows up in the last block, the moment anybody asks the history a question by name.

Read the first of those three searches as a person would read it, because that is the point. A program asks the world row what the ground is called, searches the chronicle for that name, and gets forty rows starting on tick 147,150. Forty rows. The history of this valley, as answered by the only question anybody would think to ask, is forty years long and begins in the middle. There is no error, no warning, no null and no empty result to notice. There is a plausible answer that is half the truth, and the half it left out is the half where the valley filled up.

The second search is the other half sitting right there, forty-one rows of it, ending on the tick before the rename. Forty-one and not forty because one of the eleven founding lines is the note about the ground, and it carries the old name too, which is the founding document turning up in the wreckage as evidence.

The third is the obvious repair and it is where the real lesson is. Set case aside and the search finds all eighty-one rows: nothing is missing any more, the first tick is nought and the last is 287,550, and a program could print the lot. What it cannot do is say when the name changed. No column records which vocabulary a row was written under, because no schema would deliberately design one, and there is no way to recover it from the rows themselves except by reading the text and noticing the letter. The rename put a boundary in the history and left nothing behind that names it.

$ podman exec world-db psql -U world -d world -c "SELECT entry, tick, text FROM chronicle WHERE kind = 'count' AND tick BETWEEN 139000 AND 151000 ORDER BY entry;"
 entry |  tick  |                                text                                
-------+--------+--------------------------------------------------------------------
    50 | 139950 | year 39: 236 hobbs standing in The Hollow, 1771 born and 1564 gone
    51 | 143550 | year 40: 235 hobbs standing in The Hollow, 1773 born and 1567 gone
    52 | 147150 | year 41: 237 hobbs standing in the Hollow, 1775 born and 1567 gone
    53 | 150750 | year 42: 237 hobbs standing in the Hollow, 1780 born and 1572 gone
(4 rows)

Four consecutive rows of one world's history, and the seam runs between entry 51 and entry 52. Nothing on either side of it is wrong. Entry 51 is a true sentence about year forty and entry 52 is a true sentence about year forty-one, and all that has happened is that they are sentences in two different languages about one place. Work back from that to the cause and it is one step: a name that goes into text is data at the moment it is written, and changing where names come from afterwards does not go back and change what has already been written, because nothing here ever goes back.

The operator's options in the fortieth year have to be stated plainly in the fortieth year, because the answer is short and unwelcome. They could keep the name they chose, which is what the world row was already telling them. Or they could found a second world from the new document, with its own eleven rows at its own tick zero and its own history from there, and leave the first one where it is. There is no third thing. The valley in the second world is not this valley, has none of these animals in it and shares no line of this chronicle, and that is not a shortcoming of the design: it is what having decided something means.

So the repair is not a better search, not a version column and not a migration that rewrites forty years of prose. Fixing it by rewriting the rows would destroy the one property the chronicle has, which is that an entry says what somebody said on the tick they said it. Fixing it by keeping both names and dating the change means the world row stops holding one document and starts holding a history of documents, a second chronicle inside the first one, and every question anybody asks it needs a tick attached to be answerable at all. Both of those are real designs, both cost more than the whole of this page, and neither is necessary, because there is a repair that costs nothing: the names go in at tick zero and they do not move.

Why names have two jobs

The idea underneath this page is that a name has two jobs and they pull in opposite directions. It is a label, which wants to be current and correct and improvable. And it is a piece of data written into a permanent record, which wants never to change, because everything already written down assumed it. A system that treats a name as the first thing and stores it as the second thing is a system that will one day have two histories and no way of telling them apart. Fixing the name at the founding is the cheapest way of making the two jobs the same job.

The second idea is where the fixing happens, and structure does it where discipline would have been asked to. There is nowhere else to read a name from. The file is read once and closed, the world row is written once and hangs a foreign key off itself, and every process that wants to know what the ground is called asks the same column. An operator who edits the file gets nothing, and the program that would have to be written to make the edit take effect is a program somebody would have to sit down and write on purpose. That is what a rule made out of structure looks like, and it survives the afternoon everybody has forgotten the reason.

The third is the split between the blob and the rows, and it is the schema page's argument arriving in a place no query expected it. The same document went to two destinations in one transaction, and the two are not copies. One is a vocabulary, reached by key, answering the question what is this called, which has exactly one answer at any moment. The other is history, reached in order, answering the question what happened, which has as many answers as there were moments. Storing the vocabulary as history would mean asking a question about time to print a place name. Storing the history as a vocabulary would mean throwing away everything except the most recent. Both go in, they go in together, and neither goes in twice.

The fourth is the argument for the eight keys nothing reads, and it generalises further than a world does. Any record that has a founding has this property: there is a window in which a thing can be written as part of the founding, the window is short, and it shuts without announcing itself. Afterwards the same words can still be written down, but they will carry the date they were written, which is the correct behaviour and is also the thing that makes them a different kind of statement. That is the whole reason to draft a document like this while the world is young and mostly empty. The cost of a line you turn out not to need is a line. The cost of a line you needed is the founding.

And the last one is about what the founding document is not doing, which people expect it to be doing and it never does. It does not configure anything. Not one number in this world comes out of it, no behaviour changes because of it, and a valley founded from an empty document runs tick for tick identically to a valley founded from this one. It is a record of what an operator decided, written at the only moment the decision was fresh, in a place the record will outlive them both.

None of this is peculiar to worlds. Anything that writes sentences into a permanent record has a founding document whether or not it has a file for one: the product name in a log line, the hostname baked into an audit row, the term a schema uses for a customer. Every one of those is a name chosen once, written into text thousands of times, and then, some years later, improved. The improvement is always small, always defensible and always applied going forward, because applying it backwards would mean rewriting the record, which everybody correctly refuses to do. What is left is a corpus in two vocabularies with the boundary unmarked, and a search that answers confidently and short. This world gets to make the rule structural because it is small enough to have exactly one place where a name lives. Most systems are not, and the lesson travels anyway: the cheap moment to argue about a name is before anything has been written down using it.

✓ Checkpoint — the document, the founding and the seam
  • Three of the document's eleven names go on a line this world already prints and eight name things the valley does not have. Make the case for writing all eleven at tick zero without appealing to anything that happens after it.
  • The world row and the eleven chronicle rows are written in one transaction. Name the two states that makes unreachable, and say what a restart would have to do about each of them if they were reachable.
  • All eleven founding rows carry the same kind and their order comes from entry. Say what is lost if the eleven are ordered by tick instead, and where the file's own shape is recorded.
  • The bench was handed an edited document on disk and printed the old name anyway. Say which read decides that and what would have to change for the edit to take effect.
  • A search of the chronicle for the current name found forty rows of an eighty-year history, and a case-folded search found eighty-one and could not date the change. Say what would have to be in a row for either search to answer the question properly, and what that would cost every other query.
  • many is written into the document rather than worked out from one. Give the failure that avoids and say why it is not caught by testing.
⚡ Exercises — try first, then reveal
Exercise 1 — name your own world. Replace all eleven answers in configs/genesis.json with your own, run the draft mode, and get it past every check before you found anything with it.

Do the names first and the notes second, in one sitting, and do not let yourself stop halfway. Then run go run ./cmd/genesis -mode draft and read the word count: if you are under about three hundred your notes are labels, and if you are over five hundred go back and find the sentence that started explaining rather than recording.

Two traps to watch for while you write. The first is a name you cannot say to somebody without spelling it, which you will be typing into search boxes for years. The second is the drift in register: read each note and ask whether it could have been written by somebody who had recently built the thing, or whether it has started sounding like somebody explaining a world they found. If a sentence would be at home in a myth, cut it and write down what you actually did.

Exercise 2 — give the founding rows their keys. All eleven carry kind = 'genesis'. Change the founding to write the key as the kind instead, run it, and then decide whether you want it.

The change is two lines: give Doc.Lines the key as its Kind, and change what Told is asked for. Run -mode found against a fresh database and the eleven rows come back labelled world, ground, living and so on, which is undeniably more useful to read.

Then work out what you have done to the table. kind is the chronicle's own word for what sort of thing happened, and every other row in the world is going to use it for that. Eleven of them now use it for the layout of a file that was read once. A query asking for everything that happened at the founding has to know all eleven key names, and it stops working the day the document has twelve. The version on the page needs one string and never changes. Whichever way you go, the exercise is to notice that this is a real trade and not a matter of taste: one design puts the file's shape in the history, the other keeps it in the blob where the names are.

Exercise 3 — rename it earlier. The seam on this page runs through the fortieth year of eighty. Put it in the fifth year of ten, which takes about ten seconds, and predict the two row counts before you look.

Restart the database and run go run ./cmd/genesis -mode years -years 10 -at 5. Twenty-one rows, eleven of them the founding, and the split is five rows carrying the new name against six carrying the old one, for exactly the reason it was forty against forty-one on the long run: the founding note about the ground is on the old side of the seam and always will be.

The number that does not change is the interesting one. However early or late you put the rename, a search by the current name misses everything before it, and a search that finds everything cannot say where the seam is. Move the rename to the first year and the damage is one row; move it to the last and it is one row the other way. There is no year in which renaming is free, and the only tick on which the name can be set without splitting anything is the one before there was anything to split.

The world has a name now, and so does its ground, and so do the animals walking about on it. Seven tables, and every one of them has held something on some page of this volume: a world and the eleven lines it was founded with, an archive of births and the deaths that closed them, a register of species, and a save that can put a valley back exactly the way it was. All of it was made by somebody sitting at a terminal, typing one command and waiting for it to finish. Every valley in this book so far has run for exactly as long as somebody was watching it, and stopped the moment they closed the window.