The outcome record
One scored visit kept whole: the interaction that was measured, the fitness it produced, the genome behind it, and the frame it was measured in.
An outcome record is one scored visit kept whole: the interaction that was measured, the fitness that interaction produced, the genome that was on screen, and the frame the measurement was taken in. A fitness is one number and states only how good a structure was, never how it was used, so two structures used in completely different ways become the same point the moment they score the same. The record is what the scorer would otherwise discard, written down instead, stamped with the schema version it was written under.
Record contents
Two populations on the site produce records. Both are emergence loops in which the measurement is kept rather than discarded. The exotic regions breed a structure genome and score one visit to it. The instrument breeds a voice and scores one generation of six genomes against what the player is doing.
OutcomeRecord in src/exotic/types.ts is the first of the two. It carries no storage key: where
a record was written is a property of the log rather than of the record.
| Field | What it holds |
|---|---|
v | The outcome schema in force when the record was written. |
writtenAt | Epoch milliseconds, supplied by the writer. No score reads it. |
genomeId | The pool’s own id, unique only inside the pool that minted it. |
genomeHash | A hash of the serialised genome: the identity that survives a pool reset. |
genome | The serialised genome with code forced to null, so a dead structure can be rebuilt from the record through parseGenome. |
bodyHash | A hash of the bred shader body, or 0 for a genome that has none. |
diagonalPx | The region’s own diagonal at score time. |
regionPx | The region’s width and height in CSS pixels. |
cellIds | Every cell the region offered, in the order the recorder was given them. |
interaction | The visit itself, as scored. |
fitness | The number the scorer produced from that visit. |
The interaction is what createRecorder collected while the region was mounted: milliseconds
mounted and visible, pointer path length in CSS pixels, a count of pointer move events, the list of
acquisitions, milliseconds hovered per cell without committing, the visitor’s explicit verdict when
one was given, the worst contrast ratio any cell rendered at, the largest number of cells that ever
overlapped, and whether the region was left with no interaction at all. One acquisition holds the
cell taken, milliseconds from mount, milliseconds from that cell’s first hover, how many times the
pointer entered and left before committing, and where the pointer was when the click landed, in
0..1 of the region’s own width and height. A keyboard activation and a tap with no prior pointer
movement carry a null position rather than borrowing the target’s centre.
A trial is recorded when it clears one of three bars: an acquisition happened, a verdict was
stated, or the region was mounted at least 1200 ms. Each side of a duel writes its own record and
both are appended in one call, because a duel closes two sides at once and a log this size must not
be parsed and re-serialised twice for one trial. The record is assembled from a single
outcome() reading, which takes one snapshot and scores it, rather than from a snapshot and a
separate score: the clock runs between two readings and an open dwell grows with it, so a record
built from the first reading would not have produced the number in the second.
The stored frame
Two of the record’s fields cannot be recovered from the interaction and are therefore kept beside
it. The first is cellIds, which says what was on offer and in what order, and without which a
cell id in an acquisition is a name with nothing to compare it against. The second is diagonalPx.
Path length enters the score only as a ratio against the region’s own diagonal, so a reader
checking a stored record needs that exact number. Recomputing it from regionPx is not equivalent:
Math.hypot is not required to be correctly rounded, so a recomputed diagonal can differ from the
one the scorer used in the last bit and move the score it was supposed to be verifying. A reader
with no diagonal at all falls back to the module’s DEFAULT_DIAGONAL_PX of 1200 and lands somewhere
else entirely.
The property that follows is that a stored record reproduces its own score. scripts/test-exotic.mjs
asserts it directly, over a defective visit and a clean one, by serialising a record, parsing it
back and requiring the score of the parsed interaction against the parsed diagonal to be identical
to the fitness the record carries. The same file carries the negative control: scoring the same
visit without the stored diagonal produces a different number. The instrument holds the same
property by the same rule. scoreOutcome in src/instrument/evolve.ts rebuilds the champion’s
genome from the record and replays it through the fitness function that produced it, and
src/instrument/archive.ts rounds only the four columns that never enter the score, because a
rounded genome cannot reproduce the score it was given.
Schema versions
Every record carries its own version. A bump does not corrupt the records already written, it declares that they are no longer comparable with the new ones, because a vector derived under one schema is not a coordinate in the same space as a vector derived under the next.
- The exotic outcome schema stands at 1. It versions one record’s field meanings, and is not the population’s storage version, which versions the pool’s payload; the two move for different reasons.
- The instrument outcome schema stands at 2. Version 1 records were a shorter drift vector scored by a four-term function, and the fifth term makes them incomparable rather than merely older.
- The container versions itself separately. The exotic log’s envelope and the instrument’s store file each carry a version describing the file’s layout and nothing about the records inside it.
A version a reader does not recognise is never reinterpreted. The exotic reader looks the version up in a registry of upgrade functions, which ships empty, and skips any record it finds no lift for. Reinterpreting a field under a new meaning is silent and produces coordinates that look valid, which is exactly the failure a schema version exists to make impossible. A record from a newer schema is skipped and left in place rather than deleted, and the only thing that ever removes an entry is the oldest-first trim. The instrument is stricter about the container and equally lenient per record: a store file under an unknown layout is dropped whole, while a single record under an unknown schema is dropped alone.
The two logs
Both logs live in the visitor’s own browser, in localStorage, and go nowhere else. There is no
server in either loop, and an archive is whatever one person’s use has put in it. Every bound below
is a share of one origin’s storage budget.
| Exotic regions | Instrument | |
|---|---|---|
| Key | The population’s storage key plus .rec | glia_records_v1 |
| Written by | closeTrial, once per side per trial | remember, once per scored generation |
| Held | 300 records, oldest first out | 200 records, oldest first out |
| Second bound | A payload budget of 700000 characters, trimmed by proportion in at most three passes | A write every 16 records, so a killed tab costs at most sixteen |
| On a quota throw | One retry with the newer half of the log | One retry with the newer half of the archive |
The exotic log is a second key rather than a share of the population’s own. A full record log must never cost a visitor the structures they bred: the pool sheds shader bodies to survive a quota throw and would have to shed records instead, a payload whose envelope cannot be read is abandoned whole, and anything reading records later should not have to parse a population to find them. The log is wiped with the pool it belongs to, because ids are reissued from scratch after a reset and leftovers would collide on identity.
The instrument declines to record a generation whose population showed no spread, since a generation in which the player distinguished nothing would fill the archive with one point repeated.
The outcome vector
outcomeVector reads one record and returns 20 numbers, every one finite and in 0..1.
OUTCOME_KEYS names them in the order they are returned, and that order is part of the schema: a
vector is a coordinate only if every record agrees which axis is which. This is the input to
behaviour space, and the descriptor derived from it is what gives
a genome a measured position.
| Entry | What it reads |
|---|---|
dwell | Milliseconds mounted and visible. |
presence | Pointer move events. |
pathDiagonals | Pointer path length in region diagonals. |
acquisitions | How many clicks landed. |
acquireValue | Mean worth of an acquisition: the two clocks, divided down by reentries. |
decide | Mean decay of the hover-to-click clock. |
find | Mean decay of the mount-to-click clock. |
reentries | Mean entries and exits before a commit. |
firstAcquire | Decay of the time to the first click, or of the whole visit when there was none. |
directness | Path travelled per acquisition, against the direct and the hunting bounds. |
idleFraction | Dwell on cells never clicked, over the time mounted. |
hoverBreadth | How many of the offered cells were hovered. |
acquireBreadth | How many of the offered cells were clicked. |
acquireX, acquireY | Mean click position in the region’s own frame, 0.5 when no click had one. |
orderDrift | Distance between the order cells were offered in and the order they were taken in. |
contrast | The worst contrast ratio rendered, over the ceiling of 21. |
overlap | The largest number of cells that ever overlapped. |
verdict | 1 for kept, 0 for killed, 0.5 for no verdict. |
confidence | The visit’s evidence total, as a fraction of itself plus a half-weight. |
Several of these quantities are naturally milliseconds or counts. Fed raw to an encoder they would spend most of its capacity on a dwell-time axis with the rest as noise on it, so each is saturated through a named constant instead. The raw milliseconds stay in the record, which is where a later re-derivation reads them from. Every empty case has a stated fallback rather than a NaN or a drifting sentinel, because a visit with no acquisitions must land in the same place every time it is vectorised or it is not a coordinate at all.
The saturation constants are declared in records.ts and are not the scorer’s. Several are
numerically the same as a constant inside fitness.ts, which is a coincidence of what the two
clocks measure rather than a shared definition. They are declared again for two reasons: the
scorer’s are module-private, and importing them would tie a record’s coordinates to a fitness
weight the site is free to retune, which would move the whole archive whenever somebody adjusted
how much a reentry costs.
What runs today
Writing is live. Every exotic trial that clears the bar appends a record, and every scored instrument generation with any spread in it appends one.
Reading has one live consumer. src/exotic/population.ts walks the log once per session, keeps the
best record per genome hash, derives a descriptor from it and places that individual in a niche,
which is what the elite archive ranks its overflow tier by.
The walk is lazy and memoised, and reachable only from culling and breeding, because a parse of the
whole log on the click path beside a shader compile is the cost that would otherwise be paid every
trial.
Three things are shipped and inert.
- A first visit has an empty log. Nothing has been recorded, so no individual has a record, the descriptor of every founder is null, and the pool ranks by genotype exactly as it did before descriptors existed. The first descriptor appears after the first recorded trial.
- No encoder is trained. The committed weights artifact ships with an empty key list, so the trained branch never runs and a descriptor is the fixed seeded projection described under behaviour space.
- The upgrade registry is empty. No older schema exists to lift from yet, so the branch that would lift one is reachable and does nothing.
All three wait on records, which accumulate one visit at a time in one browser at a time.
Bound to
The source this page's claims were read against, named by symbol and pinned by the digest of the file it lives in.
| Symbol | Digest |
|---|---|
| closeTrial | unbound |
| descriptorOf | unbound |
| hydrate | unbound |
| OutcomeRecord | unbound |
| outcomeVector | unbound |
| Recorder | unbound |
| remember | unbound |
| scoreOutcome | unbound |
| test-exotic.mjs | unbound |
What links here
- Behaviour space sends readers here
- Measured position sends readers here
- The elite archive sends readers here
Near this subject
These are the subjects this one is joined to in the field. Step out of this card and they are the wells around the one you are standing in.
- Behaviour space each sends you to the other
- The elite archive each sends you to the other
- Measured position each sends you to the other
- Emergence this page sends you there