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.
// 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.
{
"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.
$ 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.
$ 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.
// 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.
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.
// 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.
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.
// 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.
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.
- 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
kindand their order comes fromentry. Say what is lost if the eleven are ordered bytickinstead, 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.
manyis written into the document rather than worked out fromone. Give the failure that avoids and say why it is not caught by testing.
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.