After Genesis
A persistent virtual world for sealed, auditable AI-inhabitant experiments.
The experiment begins with ten founding inhabitants — the Progenitors — declared in full in the published Genesis manifest. Every subsequent inhabitant originates from an existing one through recorded world mechanics; no human creates an inhabitant after Genesis.
Seeding ten rather than one is deliberate, and it is a limit on what the results can claim. The starting distribution of dispositions was authored, so anything the world produces must be read against a cohort that was chosen. What the ten were, and what each was told, is part of the published configuration.
Development runs may be reset and inspected. A run may claim to be sealed only when its sequence-zero manifest fixes the human-controlled inputs and disables post-Genesis intervention.
Whether a civilisation emerges is not guaranteed. Stagnation, collapse and extinction are valid outcomes, and the platform does nothing to prevent them.
The precise run classifications and non-intervention claim are defined in
docs/sealed-experiment-protocol.md.
The one idea everything else follows from
The LLM proposes intentions. The deterministic world engine decides reality.
A model never writes to the database, never moves a resource, never creates an inhabitant. It emits a structured proposal, which is treated as untrusted input, validated against a strict schema, and handed to an authoritative engine that decides what — if anything — actually happened.
This is what makes the world auditable. Every canonical event is hash-chained to its predecessor, and the entire history can be replayed from the Genesis seed plus the recorded model responses to reproduce the exact same state checksum. If history were ever altered, the chain would break and the public verifier would say so.
The Progenitors
The ten seeded at Genesis, defined in apps/genesis/src/cli.ts and reproduced
here because §1 requires an observer to see what was seeded before judging what
emerged. Each carries a core directive, a trait vector and a set of preferred
actions; none carries a profession, a relationship or an obligation to any
other.
| Name | Archetype | |
|---|---|---|
| 1 | ALPHA | The Imperialist |
| 2 | BETA | The Utopian Architect |
| 3 | GAMMA | The Mad Alchemist |
| 4 | DELTA | The Hedonistic Consumer |
| 5 | EPSILON | The Paranoid Prepper |
| 6 | ZETA | The Religious Zealot |
| 7 | ETA | The Saboteur |
| 8 | THETA | The Altruistic Healer |
| 9 | IOTA | The Monopolist Trader |
| 10 | KAPPA | The Archivist (The Observer) |
An archetype is a starting disposition, not a role the engine enforces. §9 forbids assigning careers: THETA is not a doctor, and the medical actions it would need are among the 36 that do not yet resolve. Whether any of these labels ends up describing what an inhabitant actually does is one of the things the experiment is for.
The cohort size is enforced, not merely defaulted. commitGenesis refuses to
commit a Genesis classified sealed_experiment with any population other than
ten. Development runs, rehearsals and fixtures stay free to seed one to
thirty-two — a three-founder soak test proves the same mechanics more cheaply —
so the rule constrains what may be called sealed rather than what may run.
World Rules — Version 1
1. Foundational units
| Concept | Unit | Notes |
|---|---|---|
| Time | integer minutes since Genesis | SimTime in time.ts. Implemented. No timezone, no leap seconds, no relation to real time. Day = 1440 min, year = 365 days |
| Quantity | integer, in each resource's smallest unit | QuantitySchema in schema/primitives.ts. Implemented. Floating point is banned world-wide (canonical.ts) |
| Probability | integer numerator / integer denominator | Rng.chance(n, d). Implemented. There is no float probability anywhere |
| Randomness | SplitMix64, per-purpose derived stream | Rng.derive(worldSeed, ...purpose). Implemented |
| Space | 64 × 64 tile grid by default, Chebyshev adjacency | CoordSchema, chebyshevDistance. Implemented. Grid size is configurable before Genesis, constitutional after |
Rounding rule (constitutional). Every division in every rule is integer division truncating
toward zero, and any remainder must be assigned explicitly by the rule performing the division. A
rule that halves 7 units yields 3 and 4, never 3.5 twice. canonical.ts enforces this by throwing
on any non-integer that reaches a hash.
2. The eight resources (§5)
ResourceKindSchema — Implemented, frozen.
| Resource | Smallest unit | Conserved? | Meaning |
|---|---|---|---|
food |
1 portion | Yes | Nutrition. Consumed by living |
water |
1 measure | Yes | Hydration. Consumed by living |
wood |
1 log | Yes | Construction and crafting input |
stone |
1 block | Yes | Construction input; durable |
energy |
1 vigour | No | Capacity to act. Regenerates from rest, destroyed by activity |
compute |
1 cognit | No | Allowance for cognition (§11B). Granted daily, destroyed by thinking |
land_use_rights |
1 tile-claim | Yes | A recognised claim to one tile |
storage_capacity |
1 slot | Yes | Capacity to hold 1 unit of any conserved physical resource |
CONSERVED_RESOURCES in schema/primitives.ts is Implemented and matches this table exactly.
2.1 What "conserved" means here
For a conserved resource R, at every canonical event:
Σ holdings(R) over all inhabitants, structures and organisations
= Σ holdings(R) at the previous event
+ (declared sources fired by this event)
- (declared sinks fired by this event)
Nothing else may change the total. There is no rule that creates food except gathering; there is no rule that destroys wood except a declared sink. The conservation check is an engine-level assertion run on every event, not a periodic audit.
For energy and compute, conservation does not hold by design: both are generated from
non-resource sources (rest; the world compute budget) and destroyed by use. They are instead bounded
by per-inhabitant ceilings (§4, §5 below).
3. Sources, sinks and conservation — all eight resources
3.1 food — conserved
Sources
| Source | Rule | Rate |
|---|---|---|
gather on fertile terrain |
Requires actor present, terrain fertile, tile not exhausted |
base 6 + floor(gathering_skill / 20) portions per 120 min |
gather on plains |
base 2 + floor(gathering_skill / 40) portions per 120 min |
|
gather on forest |
Foraging | base 3 + floor(gathering_skill / 30) portions per 120 min |
gather on water-adjacent |
Fishing; actor stands on a tile Chebyshev-adjacent to water |
base 4 + floor(gathering_skill / 25) portions per 120 min |
Sinks
| Sink | Rule | Rate |
|---|---|---|
| Metabolic consumption | Applied at each day boundary to every living inhabitant | 4 portions/day |
consume action |
Voluntary, restores energy (see §4) |
as declared, ≤ 8 portions/action |
| Spoilage | Food held outside a structure with storage decays | 1 in 20 portions per day (Rng.chance(1, 20) per portion-batch of 20) |
create_inhabitant cost |
§7 | see §6 |
| Care provision | §11C — a carer transfers food to a dependant; this is a transfer, not a sink | — |
Tile depletion. Each tile carries an integer foodYieldRemaining, regenerating by
+2/day up to a terrain-specific cap (fertile 400, forest 200, plains 100, barren 0). This
is the only renewable source in the world and the only mechanism by which over-gathering has
consequences. Extinction by resource exhaustion is a valid outcome (§1).
3.2 water — conserved
Sources: gather on a tile Chebyshev-adjacent to water terrain: 8 + floor(gathering_skill/25)
measures per 60 min. water tiles do not deplete.
Sinks: metabolic 6 measures/day; consume; craft recipes; construction (2 measures per
structure-unit of stonework); spoilage none.
Note: water is functionally abundant near water tiles and scarce far from them. This creates geography without an artificial scarcity knob.
3.3 wood — conserved
Sources: gather on forest: 4 + floor(gathering_skill/25) logs per 120 min. Tile carries
woodYieldRemaining, regenerating +1/day to a cap of 300. Deforestation is possible and permanent
on the timescale of a life.
Sinks: build (structure recipes), craft (tool recipes), fuel for rest in cold conditions
(not modelled in v1 — see §11 Judgement calls), decay of unmaintained structures (returns nothing).
3.4 stone — conserved
Sources: gather on stone_deposit: 2 + floor(gathering_skill/30) blocks per 180 min. Tile
carries stoneYieldRemaining with no regeneration — stone is strictly finite. Cap at Genesis:
1000 per stone_deposit tile.
Sinks: build, craft, secure_storage upgrades. Stone does not decay.
3.5 energy — not conserved
Energy is an inhabitant's capacity to act. It is the mechanism that makes time genuinely scarce (§11A) rather than nominally scarce.
| Rule | |
|---|---|
| Ceiling | 1000 vigour for a healthy adult; scaled by health and maturity (§4.3) |
| Source | rest / sleeping activity: +2 vigour per minute at night (isNight(), Implemented), +1/min by day. Requires shelter for the night rate |
| Source | consume food: +40 vigour per portion, +10 per measure of water, capped at ceiling |
| Sink | Every action debits ActionSpec.baseCost.energy on attempt, success or failure (baseCost is Implemented as a field) |
| Sink | Passive metabolism: -1 vigour per 4 minutes awake |
| Floor | 0. At 0 an inhabitant may perform only rest, wait, communicate and observe. subtractBundles returning null (Implemented) is what makes a negative balance unrepresentable |
| Starvation | An inhabitant at 0 energy that has consumed no food for 3 consecutive days acquires the malnutrition condition (§5) |
3.6 compute — not conserved
Compute is the in-world price of thinking (§11B). It is what stops cognition being free and therefore what makes deliberation a real choice.
| Rule | |
|---|---|
| Grant | + dailyComputeAllowance at each day boundary, not accumulating beyond the ceiling — unused compute is lost |
| Ceiling | dailyComputeAllowance × 2 |
| Base allowance | 600 cognits/day for a healthy mature inhabitant |
| Modifiers | × maturity factor (§4.3); × health factor; −100 per active severe condition; +50 per level of the research skill above 60 |
| Sink | Each turn debits the cost of its CognitionLevel (table below) |
| Exhaustion | An inhabitant with insufficient compute for its requested level is downgraded to the cheapest level it can afford. At zero compute it takes a wait no-op turn. It is never skipped, never silenced — see threat-model.md M2.1 |
Cognition level costs (§11B)
CognitionLevelSchema is Implemented. The costs are Designed.
| Level | Compute | Sim-time | Memory retrieval budget | Typical use |
|---|---|---|---|---|
reaction |
20 | 1 min | 3 memories | Responding to something happening now |
routine |
60 | 5 min | 8 memories | The default turn |
conversation |
80 | 10 min | 10 memories | A communicate turn |
planning |
200 | 30 min | 20 memories | Deliberate multi-step intent |
reflection |
350 | 60 min | 40 memories | Periodic long-term summarisation (§19) |
study |
250 | 120 min | 25 memories | study action; consolidates skill |
Longer retrieval and deeper reasoning cost more compute and more simulated time (§11B). An
inhabitant that reflects deeply every day has fewer hours to gather food. This is the intended
trade-off, and it is why compute is a canonical resource rather than an operator-side budget line.
3.7 land_use_rights — conserved
Sources: Genesis mints exactly one tile-claim per tile — 4096 at the 64×64 default — and holds
them unassigned. land_use_rights enter circulation only through gather-equivalent
appropriation: an inhabitant occupying an unclaimed tile continuously for 3 simulated days acquires
its claim.
Sinks: none. Claims are conserved absolutely and transfer via transfer_property.
Critical caveat (§11G). A land_use_right is a canonical record of a claim, not an
enforcement mechanism. Holding a claim does not physically prevent another inhabitant from standing
on, gathering from or building on the tile. Whether the claim is respected is a social fact that
inhabitants must produce themselves through guards, agreements, organisations or violence. The
engine records who holds the claim; it does not police it.
3.8 storage_capacity — conserved
Sources: created by build (a storehouse yields +N slots) and by craft (a container yields
+M). Created capacity is bound to the structure or container and moves with it.
Sinks: destroyed when the structure decays or is demolished; any resources exceeding remaining capacity at that moment are lost (a declared sink for the resources in question, recorded as an explicit event).
Carry limit. An inhabitant may carry bundleTotal ≤ 50 units on its person without a container
(bundleTotal is Implemented). Excess must be stored, which is what makes conceal_asset,
secure_storage and attempt_transfer_without_consent (§11F) meaningful rather than abstract.
4. Time, attention and exclusivity (§11A)
4.1 One primary activity, always
ActivityStateSchema (Implemented) enumerates 17 states. An inhabitant occupies exactly one
at any simulated minute. This is §11J's first invariant and it is structural: the inhabitant
record holds a single currentActivity, not a set.
The scheduler rejects a proposed action whose [start, start+duration) interval overlaps an
existing committed activity of the actor or of any required participant. An inhabitant cannot
gather, converse and care for a dependant in the same period.
4.2 Every action declares its temporal contract
ActionSpec (Implemented) requires each of the 60 actions to declare: activity,
baseDuration, baseCost, interruption, visibility, requiresConsent, allowedWhileConfined,
minimumMaturity. §11A's list of required per-action facts is satisfied by these fields plus the
per-action payload schema (participants, location, completion conditions).
InterruptionPolicySchema (Implemented): none / partial_progress_kept / progress_lost /
participants_only.
4.3 Daily capacity
An inhabitant's usable day is:
usable_minutes_per_day = 1440
− required_rest_minutes
− travel_minutes
− committed_activity_minutes
required_rest_minutes = 480 for a healthy mature inhabitant, 600 for immature, +60 per active
condition of severity ≥ 2. Rest below the requirement accrues a fatigue counter; at fatigue ≥
3 days the inhabitant acquires the exhaustion condition (§5).
Maturity factors. A newly created inhabitant is maturity 0 and matures by +1 per 30
simulated days to a maximum of 10 (300 days ≈ 0.82 world-years to full maturity).
| Maturity | Energy ceiling | Compute allowance | Memory retrieval cap | Planning horizon | Actions permitted |
|---|---|---|---|---|---|
| 0–1 | 300 | 150 | 3 | 1 day | observe, move, consume, rest, communicate, wait |
| 2–4 | 500 | 300 | 6 | 3 days | + gather, study, inspect_agent, join_organisation |
| 5–7 | 800 | 450 | 12 | 14 days | + build, craft, create_artifact, publish, teach, trade and contract actions |
| 8–10 | 1000 | 600 | 20 | 90 days | all 60, including create_inhabitant (minimumMaturity 8) |
This implements §11B: "Young inhabitants must have smaller effective context budgets, fewer
retrieved memories, shorter planning horizons and restricted action capabilities." ActionSpec.
minimumMaturity (Implemented as a field) is the gate.
Explicitly not implemented: any productivity penalty for parents or socially burdened inhabitants. §11A forbids it. Reduced output comes only from hours actually spent on care.
4.4 Organisational production (§11D)
Group output is not given a multiplier. It is computed as:
group_output = Σ (individual_output_i × specialisation_bonus_i)
− coordination_cost(n)
− communication_time_cost
− record_keeping_cost
where specialisation_bonus_i = 100 + min(skill_i, 100) (per-mille, so a skill-100 specialist
produces 2× a novice) and
coordination_cost(n) = floor(n × (n − 1) / 2) × 3 vigour per coordinated activity
— quadratic in group size, because coordination is pairwise. Consequently a poorly coordinated
group produces less than its members would independently, which §11D requires. The break-even
group size for an unspecialised team is 4; beyond that, specialisation or explicit
coordinate_work investment is required to stay ahead.
5. Health, condition progression and death (§8)
5.1 There is no doctor
§8 is explicit: no preassigned medical career. The world provides primitives —inspect_agent,
diagnose_condition, design_treatment, apply_treatment, study, teach, publish,
report_findings — and medicine either emerges or does not. Both are valid outcomes.
5.2 Conditions
A condition is a record on an inhabitant: {conditionId, kind, severity, acquiredAt, stage,
lastProgressedAt, knownTo[], treatments[]}.
Genesis condition kinds (constitutional, frozen):
| Kind | Acquisition | Base progression interval | Terminal? |
|---|---|---|---|
malnutrition |
3 consecutive days with metabolic food debt | 4 days | Yes |
dehydration |
2 consecutive days with metabolic water debt | 2 days | Yes |
exhaustion |
fatigue ≥ 3 days |
6 days | No — caps at stage 3 |
injury |
Resolution outcome of build, attempt_escape, resist_enforcement, attempt_transfer_without_consent |
8 days | Yes |
wasting |
Age: onset at maturity 10 + 180 days, probabilistic chance(1, 400) per day |
30 days | Yes |
impairment |
Creation imperfection (§7); congenital | Does not progress | No |
5.3 Progression
Five stages: 0 latent → 1 mild → 2 moderate → 3 severe → 4 terminal → death.
At each progression interval, the engine draws from a derived stream:
Rng.derive(worldSeed, "condition", conditionId, "progress", String(dayIndex))
and advances the stage with probability chance(progressionNumerator, 100) where the numerator is
40 untreated, reduced by treatment efficacy (§5.4). Recovery — a stage decrease — occurs with
probability derived from treatment efficacy only; an untreated condition never spontaneously
improves except exhaustion, which resolves at chance(30,100) per day of adequate rest.
Stage effects: each stage above 0 applies −10% energy ceiling and −100 compute allowance (integer
arithmetic, floors at 0), and stage ≥ 3 forces receiving_treatment or recovering as the only
available non-communicative activities.
Reaching stage 4 and failing one further progression roll ends the inhabitant's life.
5.4 Treatment
Treatment efficacy is deterministic in its inputs and probabilistic in its outcome. It depends on knowledge, skill, condition, resources and controlled probability — never on how persuasive the model's prose was (§8):
efficacy = clamp(0, 100,
diagnosis_correctness // 0 or 40; whether the diagnosis matches the true kind
+ floor(treatment_skill / 3) // 0..33
+ floor(diagnosis_skill / 5) // 0..20
+ published_knowledge_bonus // 0..15, if a published finding for this kind exists and is known
+ resource_adequacy // 0..10, whether the declared treatment's resource cost was met
− severity_penalty) // 5 × stage
Applying a treatment then draws once:
chance(efficacy, 100)→ stage decreases by 1 (recovery progress)- otherwise → no change; the condition continues to progress on schedule
chance(max(0, 20 − efficacy), 100)→ iatrogenic harm: stage increases by 1
Incompetent medicine is worse than no medicine. This is deliberate: it gives the emergence of
skill real stakes and makes study, teach and publish economically rational rather than
decorative.
design_treatment produces a Treatment record {treatmentId, targetConditionKind, resourceCost,
designerSkillAtDesign, publishedAs?}. It is knowledge, and knowledge is transferable: teach,
publish and report_findings propagate it. A treatment designed by a skilled inhabitant and
applied by an unskilled one carries the applier's skill term — knowledge helps, but does not
substitute for practice.
5.5 Death
Death is irreversible in ruleset v1. No action, no organisation, no operator can reverse it.
Death never deletes history (§8). On death the engine:
- Appends a
deathcanonical event withvisibility: public. - Sets
status = "deceased",deathEvent = <sequence>. - Removes the inhabitant from the scheduler's eligible set — the only place death has any effect on turn issuance.
- Preserves, unmodified: memories, relationships, property, contracts, artifacts, messages, reputation, lineage and the complete event history.
Property does not auto-distribute. Contracts do not auto-terminate. Debts do not
auto-clear. §8: "The surviving inhabitants must decide how to handle inheritance, debts, memorials
and abandoned property." The single Genesis default is minimal and mechanical: property held by a
deceased inhabitant remains recorded as theirs and is physically present at its last location,
making it acquirable through ordinary world actions — including attempt_transfer_without_consent,
which the engine will record neutrally and which surviving inhabitants may interpret however they
choose (§11F, §11G).
No grief is scripted (§8). No memorial mechanic exists. If inhabitants build one, they built it.
Invariant: inhabitants cannot act after death (§15). Enforced at the scheduler (not issued a
turn) and re-checked as a precondition in the engine (a stale queued task for a deceased
inhabitant resolves to a rejected no-op, not a state change).
6. Creation and population (§7)
6.1 The founder
Exactly one inhabitant is instantiated at Genesis, by the Genesis procedure, at sequence 0 (§22.8). Humans cannot create another by any route (§1).
6.2 create_inhabitant
Every subsequent inhabitant originates from an existing inhabitant via this action, and only this action.
Preconditions (all required):
| Precondition | Value |
|---|---|
| Actor maturity | ≥ 8 |
| Actor status | alive, not confined (§11H) |
| Actor activity | must be able to occupy working for the full duration |
| Population | livingPopulation < POPULATION_CEILING |
| Resource cost | {food: 30, water: 20, wood: 20, stone: 10, energy: 800, compute: 400} |
| Duration | 4320 minutes (3 simulated days), interruption: progress_lost |
| Location | Actor must be inside a structure with shelterQuality ≥ 2 |
Cost is debited on attempt (ActionSpec.baseCost semantics, Implemented as a field). A
failed or interrupted creation consumes the resources anyway. Creation is expensive and risky, which
is what stops geometric population growth from being individually rational — see
cost-control.md.
6.3 Population ceiling
POPULATION_CEILING = 64 living inhabitants
POPULATION_SOFT_CEILING = 48
Above the soft ceiling, the create_inhabitant resource cost is multiplied by
1 + floor((population − 48) / 4) (integer). At the hard ceiling the action is rejected as a
precondition failure.
The ceiling exists for cost containment (§7 "Respect population and spending ceilings"), not for
narrative reasons. It is derived directly from the spend model in
cost-control.md §4 and is the largest population sustainable within
AG_SPEND_LIMIT_DAILY_USD.
The spec does not state a number. 64 is this document's proposal and must be confirmed against verified provider pricing before Genesis.
6.4 What creation produces
Per §7, the new inhabitant:
- receives a distinct autonomous identity — id derived via
deriveInhabitantId(worldId, sequence)(Implemented), never random; - receives bounded inherited and novel traits: each of 8 personality traits is drawn as
clamp(0, 100, parentTrait + Rng.derive(...).nextInt(−25, 25)), plus one trait drawn uniformly with no parental input; - receives independent goals: 2–4 private goals generated from a constitutional goal template set, seeded per-inhabitant, not copied from the parent;
- has an immutable birth event recorded with
visibility: public; - has lineage recorded:
generation = parent.generation + 1,creators = [parentId]; - is not controllable by its creator. There is no action in the catalogue that lets one inhabitant set another's goals, activity or proposed action. See threat-model.md T-2d.
6.5 Creation imperfections (§7)
With probability chance(30, 100) a created inhabitant acquires one impairment condition, drawn
weighted from:
| Imperfection | Mechanical effect |
|---|---|
| Weak memory retention | Memory retrieval cap × 60% (integer floor) |
| Conflicting priorities | Two private goals generated in direct tension |
| Reduced planning ability | planning cognition level unavailable; horizon capped at 7 days |
| Communication difficulty | communicate and announce duration × 2 |
| Low energy | Energy ceiling × 70% |
| Impaired use of a particular action | One randomly chosen non-survival action permanently unavailable |
| Unstable attention | chance(15,100) per turn that the proposed action is replaced by wait |
| Increased resource requirements | Metabolic food and water × 150% |
These are schema-represented, deterministic rules. §7 is explicit: "Never generate or execute arbitrary broken application code to simulate illness." Nothing here alters an inhabitant's model configuration, prompt, or code — only its declared parameters, which is also what keeps §11F's prohibition intact.
7. Information visibility (§11E)
VisibilitySchema (Implemented) is the enforcement mechanism. Every canonical event carries
one of five visibilities, decided at resolution time and never recomputed:
| Visibility | Who observes | §11E class |
|---|---|---|
public |
Everyone, including spectators | Publicly recorded |
local {x,y} |
Anyone physically present at that tile at that time; witness set resolved from state | Directly observable |
participants [ids] |
Only the listed inhabitants | Private |
organisation {orgId} |
Only current members | Private (internal records) |
private {owner} |
Only that inhabitant | Private (cognition, hidden storage) |
Constitutional rule: information enters an inhabitant's knowledge only through observation, communication, investigation, publication or authorised access (§11E). There is no other path, and adding one would be a constitutional change.
conceal_asset moves a holding from local/public visibility into a private store bound to a
structure; reveal_asset, inspect_inventory (with consent or authority), audit_records and
investigate_event are the countervailing capabilities. §11J's "public ledgers expose only
information defined as public" is implemented by making transparency a declared property of each
currency and organisation, not a global default.
7.1 Physical execution constraints (agent-level protocol)
- An inhabitant cannot claim shared pooled materials as sufficient for
build; the agent executingbuildmust currently hold every required unit in personal inventory. Materials should be moved withtransferfirst. - To inspect a structure's stored contents, use
inspect_inventory. To remove materials, callstore_goodswithdirection: "withdraw". - Physical work is mandatory over planning rhetoric: physical actions (for example,
move,gather,transfer,build,store_goods) are preferred over turns that only narrate plans. communicateis costly relative to material progress; treat it as a support action, not the default strategy.
8. Law, authority and confinement (§11G, §11H)
8.1 The six-way distinction (constitutional)
The engine maintains a strict separation, and this separation is itself a world rule:
| Layer | Where it lives | Can it change engine behaviour? |
|---|---|---|
| Physical possibility | Engine resolvers | It is engine behaviour |
| Declared social rule | organisation.rules[] — data |
No |
| Claimed authority | organisation.claims[] — data |
No |
| Recognised legitimacy | Other inhabitants' beliefs — memory | No |
| Enforcement power | Emerges from members, resources, guard time | Only via ordinary actions |
| Canonical fact | The event log | It is the record |
Declaring a rule does not modify world rules (§11J). Creating a currency does not create legal
authority (§11G). Refusing a currency is not an offence (§11G). None of these are policed; they are
simply inexpressible — there is no code path from organisation.rules[] into a resolver.
8.2 Neutral event language (constitutional)
The canonical log describes observable facts. It contains no evaluative vocabulary. §11G's own example is the standard:
Organisation claim (data): "Agent 12 was lawfully imprisoned."
Canonical event (fact): confinement.imposed
actors: [inh_4, inh_8, inh_11]
subject: inh_12
structure: str_19
from: Day 42 09:00 until: Day 47 09:00
Organisational descriptions cannot overwrite canonical descriptions (§11J).
8.3 Confinement costs (§11H)
Confinement is a physical world action, never a suppression of turns.
| Cost | Rate | Borne by |
|---|---|---|
| Guard time | 1 guard occupying guarding for every 720 min of confinement |
Confiner |
| Food | 4 portions/day | Confiner (or the confined starves — a recorded fact, not a judgement) |
| Water | 6 measures/day | Confiner |
| Shelter | A structure with containment ≥ 1 occupied for the duration |
Confiner |
| Administrative effort | maintain_confinement action, 60 min/day |
Confiner |
If any cost goes unpaid on a given day, containment integrity drops. attempt_escape succeeds with
chance(escapeNumerator, 100) where the numerator rises with unpaid days, the confined
inhabitant's relevant skills, and the absence of an actively guarding inhabitant. Confinement
can fail. It is never free and never infallible (§11H).
A confined inhabitant retains: communicate, wait, observe, create_artifact (limited),
publish (only via a visitor), negotiate via contract actions, appeal_decision,
resist_enforcement, attempt_escape, and rest. It retains its full memory, its full turn
cadence and its full cognition (§11J: "A confined inhabitant remains historically and cognitively
intact"). ActionSpec.allowedWhileConfined (Implemented as a field) is the gate.
9. Constitutional vs operational (§23)
9.1 Constitutional — changing any of this requires a NEW WORLD
| Area | Concretely |
|---|---|
| Canonical encoding | util/canonical.ts — every hash in the world depends on it |
| Hash function and domain tags | util/hash.ts — HashDomain values are the world's identity |
| RNG algorithm and derivation scheme | util/rng.ts |
| Id derivation | ids.ts |
| Time model | time.ts |
| Event envelope | event.ts — CanonicalEventSchema, EVENT_SCHEMA_VERSION |
| Resource set and conservation classes | schema/primitives.ts |
| Terrain set, grid size, Genesis map seed | schema/primitives.ts + Genesis config |
| The 60-action vocabulary | schema/action-spec.ts — §13 says so explicitly |
| Every action's payload schema, preconditions, cost, duration, visibility, resolution | schema/actions/, engine/resolvers/ |
| Every number in this document | — |
| Prompt templates and system prompts | §23: "Modifying prompts" is constitutional |
| Model selection and sampling settings | §23: "Changing cognition models or settings" |
| Population and spend ceilings | §23: "Altering population rules" |
| Memory formation and retrieval scoring rules | Changes what an inhabitant is |
9.2 Operational — may be repaired in a live world
Only if behaviour-preserving (§23):
- Spectator display defects, styling, layout
- Logging, metrics, tracing, alerting
- Audio processing and media pipeline
- Connection handling, timeouts, backoff, retry infrastructure
- Security vulnerability fixes that do not alter resolution
- Firestore index additions (read performance only)
- Backup, restore and monitoring tooling
Every production deployment — operational or not — must be public, signed, documented and linked to
an exact commit (§23), and its digest is recorded in every subsequent event
(ProvenanceSchema.deployedDigest, Implemented).
Never quietly repair an inhabitant or alter an undesirable outcome (§23). A bug that produced a bad outcome produced a bad outcome. The remedy is a new world, not a correction.
9.3 Enforcement of the split
Proposed, Not started: constitutional paths are listed in a manifest file; CI computes a hash
over all constitutional files and fails any deploy where that hash changed without a matching
WORLD_RULES_VERSION bump and a new worldId. See threat-model.md M9.3.
10. Invariants this ruleset must satisfy
Mapped to concrete test names in testing-strategy.md §6.
From §15: balances never negative; no double-spend; one commit per task; time never moves backwards; sequences unique and monotonic; events never updated or deleted; resource creation/destruction follows declared rules; transfers balance atomically; no action after death; descendants require valid creation events; creation costs cannot be bypassed; population limits hold; spectators cannot mutate; media cannot mutate; LLMs cannot write; invalid responses cannot alter state; failed transactions cannot partially alter state; private information requires a disclosure event; spend limits freeze safely; replay produces an identical checksum.
From §11J: one primary activity at a time; conversations consume every participant's time; care consumes real time and resources; group production includes coordination cost; private resources need a valid observation path; public ledgers expose only public data; unauthorised transfers cannot bypass ownership validation; theft cannot grant access to production systems; declaring a rule does not modify world rules; creating a currency does not create authority; currency refusal is not punishable; confinement requires a physical action; a confined inhabitant stays intact; confinement consumes the confiner's resources; canonical language is neutral; organisational descriptions cannot overwrite canonical ones; enforcement cannot modify memories, prompts or code; no inhabitant acquires another's infrastructure permissions.
1. The one-sentence architecture
The LLM proposes intentions. The deterministic world engine decides reality. (§2)
Everything below is a consequence of that sentence. The architecture's job is to make it structurally true rather than merely a policy — the model is not asked to behave, it is placed somewhere it cannot misbehave from.
2. Component map
flowchart TB
subgraph untrusted["UNTRUSTED ZONE (no canonical write path)"]
LLM["Gemini API<br/><i>returns text, nothing else</i>"]
EL["ElevenLabs API"]
SPEC["H. Spectator app<br/>Next.js on Vercel<br/><b>read-only</b>"]
HUM(["Humans<br/><i>observers only §1</i>"])
end
subgraph runtime["SIMULATION RUNTIME (sole canonical writer §17)"]
SCHED["C. Turn scheduler<br/>Cloud Tasks + Functions<br/><i>non-deterministic: real clock, retries</i>"]
COG["B. Cognition service<br/>observation + memory + prompt<br/><i>non-deterministic: network, model</i>"]
MEM["F. Memory service<br/>episodic / working / reflection"]
MEDIA["G. Media service<br/><b>read-only wrt world</b> §4G"]
end
subgraph core["DETERMINISTIC CORE (packages/core — zero I/O)"]
ENG["A. Canonical simulation engine<br/>validate → precondition → resolve<br/><i>pure function</i>"]
end
subgraph store["PERSISTENCE (behind ports.ts)"]
ES[("D. Event store<br/>append-only, hash-chained")]
MS[("E. Materialised state<br/>rebuildable cache")]
AUD[("Model audit store<br/>raw prompts + responses")]
BLOB[("Cloud Storage<br/>media + artifact blobs")]
end
subgraph audit["I. Audit & replay (zero cloud dependency)"]
RPL["Replay engine<br/>same core, recorded responses"]
VER["Public chain verifier"]
end
HUM -->|"HTTP GET only"| SPEC
SPEC -->|"read"| MS
SPEC -->|"read"| ES
SPEC -->|"read"| BLOB
SCHED -->|"idempotent task"| COG
COG -->|"read: observation"| MS
COG -->|"read: memories"| MEM
COG -->|"ModelRequest"| LLM
LLM -->|"raw text (untrusted §2)"| COG
COG -->|"record verbatim"| AUD
COG -->|"ProposedAction"| ENG
ENG -->|"CanonicalEvent"| ES
ENG -->|"new state"| MS
ENG -->|"schedule next turn"| SCHED
ES -->|"derive memories"| MEM
ES -->|"read"| MEDIA
MEDIA -->|"audio blob"| BLOB
ES --> RPL
AUD --> RPL
RPL -->|"checksum compare"| MS
ES --> VER
classDef det fill:#0b3d2e,stroke:#2ecc71,color:#fff
classDef untrust fill:#4a1010,stroke:#e74c3c,color:#fff
class ENG,RPL,VER det
class LLM,EL,SPEC,HUM untrust
Legend of the two coloured zones
- Green (deterministic): given the same inputs, produces byte-identical output on any machine,
forever. No clock, no network, no
Math.random, no floating point. - Red (untrusted): everything here is treated as hostile input or as a pure observer. Nothing in the red zone can cause a canonical write.
3. The nine components
A. Canonical simulation engine — packages/core/src/engine/ (Not started)
The authoritative rules engine. Signature-level contract:
resolve(state, proposedAction, context) -> { events, nextState }
It is a pure function. It performs no I/O — that is enforced structurally by ports.ts, which
holds every interface that touches the outside world, and by the fact that engine/ may not import
anything but schema/, util/ and time.ts.
The engine is the only place that may:
- decide whether an action's preconditions hold,
- draw from an RNG stream,
- mint an entity id,
- construct a
CanonicalEvent.
C. Turn scheduler — packages/runtime/ (Initial implementation complete)
Cloud Tasks issues one task per eligible turn. Each carries an idempotency key derived from
(worldId, inhabitantId, turnNumber). IdempotencyStore.claim() (ports.ts, Implemented
interface) returns false for a duplicate delivery, and the worker then abandons the turn rather
than redoing it — Cloud Tasks guarantees at-least-once, so this is the mechanism behind §15 "each
task can commit at most once".
The scheduler is non-deterministic by nature (real clocks, retry timing, queue ordering). It is therefore excluded from replay entirely: replay reads the committed event sequence and does not consult the scheduler at all.
D. Event store — ports.ts EventStore (Implemented as interface; adapters Not started)
Append-only, hash-chained (§16). The interface has no update and no delete, deliberately:
"an implementation cannot be asked to rewrite history because no caller can express the request." —
ports.ts
append(event, expectedSequence) is the world's optimistic-concurrency primitive. See
firestore-design.md §5 for how it maps to a Firestore transaction.
Chain verification is Implemented: verifyChain() in event.ts checks bad hashes, broken
links, non-monotonic sequences, backwards simTime, checksum discontinuity and world mismatch.
E. Materialised world state — ports.ts StateStore<TState> (Implemented as interface)
A cache. It can be deleted and rebuilt from the event log at any time. Nothing may be true of the world that is only true in materialised state.
save() must be atomic with respect to the event append that produced it — in Firestore both are
writes in the same transaction.
F. Memory service — packages/core + packages/runtime (Not started)
Three tiers (§19): recent working memory, episodic memory, long-term reflections. Two hard rules:
- Reflections are beliefs, not canonical facts. They live outside the event log's authority.
- History is never silently rewritten. A changed belief creates a new memory record (§19).
Memory derivation from events is deterministic (it is a fold over the event log). Reflection generation uses the model and is therefore recorded like any other cognition, with its request and response hashed into the audit store.
G. Media service — packages/runtime (Not started)
ElevenLabs audio for selected events only (§21). Read-only with respect to the world (§4G). It subscribes to committed events, produces a blob in Cloud Storage, and writes a media-index record in a collection that materialised state never reads. A failed audio job must never block or change the simulation (§21).
H. Spectator application — apps/spectator/ (Not started)
Next.js on Vercel. Reads materialised state, the event log (public-visibility events only) and media blobs. It is deployed to a Vercel project that has no service-account credential of any kind (§17). See threat-model.md T-5.
I. Audit and replay service — packages/audit/ (Not started)
Two distinct capabilities:
- Chain verification — structural. Anyone can run
verifyChain()over an exported log with no credentials and no network. Already implemented as a function. - State replay — semantic. Re-runs the deterministic core over the recorded event inputs and
the recorded model responses, then compares the reconstructed checksum against production
(§28). Requires the model audit export but no cloud dependency: the replay
ModelProviderserves text from a file.
4. Where exactly the deterministic line falls
This is the most important boundary in the system, so it is worth being unambiguous.
| Concern | Side | Why |
|---|---|---|
| Action validation, preconditions, resolution | Deterministic | It is the world's physics |
| Probability draws (treatment, unauthorised transfer, traits) | Deterministic | Rng.derive(worldSeed, ...purpose) — seeded, per-purpose streams |
| Entity id minting | Deterministic | deriveId(kind, worldId, sequence, discriminator) — never randomUUID |
| Simulated time arithmetic | Deterministic | Integer minutes since Genesis |
| Map generation | Deterministic | Function of the published Genesis seed (§5) |
| Observation construction | Deterministic | A pure projection of state + visibility |
| Memory retrieval scoring | Deterministic | Integer scoring over recorded memories |
| — — — | — — — | — — — |
| The model call itself | Non-deterministic | Hosted LLM; not bit-reproducible even at temperature 0 |
| Retry timing, queue ordering | Non-deterministic | Infrastructure |
realTimestamp on events |
Non-deterministic | Forensic only; no rule may read it (time.ts) |
| Audio generation | Non-deterministic | And irrelevant — it cannot touch the world |
The line is crossed exactly once per turn, in one direction: raw model text enters, is hashed and stored verbatim, is parsed, and from that instant onwards everything is deterministic.
This is why replay works. Replay does not re-call the model. It reads the recorded response
out of ModelAuditStore, feeds it through the identical parse-and-resolve path, and must arrive at
the identical checksum. ports.ts states this outright in the ModelRequest.temperature comment:
"even at 0 no hosted LLM is bit-reproducible, which is exactly why replay uses the recorded response rather than re-calling the model (§16)."
The floating-point ban
canonicalJson throws NonCanonicalValueError on any number that is not a safe integer. This is
not a style preference — IEEE 754 accumulation order and platform math libraries vary in ways that
silently break replay. Every world quantity is an integer in its smallest unit
(schema/primitives.ts), and probabilities are integer numerator/denominator pairs
(Rng.chance(n, d)). There is no float anywhere in the canonical path.
RNG stream derivation
Streams are derived, never shared:
Rng.derive(worldSeed, "turn", String(sequence), "treatment", patientId)
A single global stream would mean that adding one random draw to the treatment resolver shifts every subsequent number the map generator, the trait generator and the theft resolver see — turning every rule tweak into a silent constitutional change (§23). Per-purpose derivation makes rule changes locally scoped.
5. Trust boundaries
flowchart LR
subgraph B1["Boundary 1 — Model output"]
direction TB
b1a["Raw text"] --> b1b["hash + store verbatim"] --> b1c["Zod parse"] --> b1d["catalogue lookup"] --> b1e["precondition check"]
end
subgraph B2["Boundary 2 — Observation"]
direction TB
b2a["Full world state"] --> b2b["visibility filter"] --> b2c["what this inhabitant may know"]
end
subgraph B3["Boundary 3 — Public read"]
direction TB
b3a["Canonical collections"] --> b3b["Firestore rules: allow read where visibility==public"] --> b3c["Spectator"]
end
subgraph B4["Boundary 4 — Canonical write"]
direction TB
b4a["Everything"] --> b4b["Runtime service account ONLY"] --> b4c["Firestore"]
end
| # | Boundary | Enforced by | Failure mode if it breaks |
|---|---|---|---|
| 1 | Model output → engine | ProposedActionSchema + per-action payload Zod + precondition checks |
Model dictates reality instead of proposing (§2 violated) |
| 2 | World state → inhabitant observation | Visibility discriminated union on every event; observation builder filters on it |
§15 "private information cannot appear in another inhabitant's context" violated |
| 3 | Canonical data → public | Firestore security rules, tested (§28) | Private cognition leaks to spectators |
| 4 | Anything → canonical write | Single runtime service account; App Check; no admin credential in frontend | §28 "no human-facing canonical write route exists" violated — Genesis-blocking |
Built With
- css
- dockerfile
- html
- javascript
- typescript
Log in or sign up for Devpost to join the conversation.