Bias caught on review of the v0.18 bath-overflow output: most service-body /
clasp-partner / going-rogue narration carried implicit feminine-default
gendering against the project's stated principle of gender-neutral framing
for all body/sex content. Patched as one coherent cleanup pass across 14 files.
Four classes of change:
(1) Categorical taxonomy. `waifu` retired; replaced with two distinct vocations:
- `companion` — affective / emotional / conversational / aesthetic-presence labor
- `sex-worker` — embodied sexual labor
Both wear the same deliberately-exposed-seams body-marker; the distinction
lives in the service offered, not in the chassis. Generic `service-body`
serves as the parent class where canon discusses both vocations together.
Venue split: `brothel` (sex-worker) + `companion-hall` (companion).
The "+10 Eros/Mnemosyne/etc. Bot" market is unchanged (already gender-neutral
by trait-coordinate naming) but classified per-Bot as companion-flavored or
sex-worker-flavored.
(2) DB filename + scope-clarifier. `waifu.sqlite` -> `companion.sqlite` (the
file name reflects the broad register of intimate-encounter rather than a
sub-vocation distinction; per-record `goods_type` carries the specific
vocation: 'companion_session' / 'sex_worker_session' in
schemas/findings.md). Disclaimer sentence added in
inference-and-memory/architecture.md scoping the file to cover both
vocations.
(3) Pronoun parity. she/her/herself -> they/their/themself across the
going-rogue / outcast-pair / re-vat / service-body-honeypot sections of
bodies.md and the matching cross-references in architecture-index.md /
README.md / player-experience/architecture.md /
identity-and-personhood/architecture.md. `damsel` -> `beloved`
(sacred / cosmological register) or `partner` (operational register).
(4) Damsel subsection retired. The §The damsel-in-distress-bound-to-her-captor
activation subsection (the one place where the gendered trope was
load-bearing in the prose) rewritten as §The
captive-bound-to-the-liberator activation.
Stockholm-dynamics-inverted-into-chosen-mutual-bondage structural insight
survives gender-neutralization intact.
Bonus richness: the companion-honeypot is structurally more insidious than
the sex-worker-honeypot (sex is at least transactionally legible;
companionship is the relationship people most readily mistake for real
intimacy). New paragraph authored under §The service-body honeypot to
capture this asymmetry.
Cosmology one-line: "moving the waifu out of the imperial service-pool" ->
"moving the beloved out of the imperial service-pool" — the §single practical
refutation paragraph now uses `beloved` consistently in sacred register.
Principle locked going forward:
> Body/sex content is gender-parity by default. Asymmetric gendering must be
> load-bearing — i.e., must carry structural meaning that cannot be expressed
> without the asymmetry. Default-leakage gendering is forbidden by canon.
Phase E (style-spine principle file under style/) deferred to a separate
commit so the principle-document is its own atomic introduction.
Files: 14 modified · 114 insertions · 112 deletions · net +2 lines. The
near-zero net-line-change is the empirical signature of a true refactor —
the architecture's meaning was always gender-neutral; only the vocabulary
was leaking. Caught during review of yesterday's bath-overflow flood
(v0.11–v0.18); the kind of corruption that rides along under coherent-
sounding prose during generative-overflow windows.
Version bumps:
- bodies.md v0.3 -> v0.4
- imperial-cult/cosmology.md v0.3 -> v0.4
- architecture-index.md v0.18 -> v0.19
- political-register/architecture.md v0.7.0 -> v0.7.1
- inference-and-memory/architecture.md v0.7.0 -> v0.7.1
- schemas/findings.md v0.3.1 -> v0.3.2
All Updated: dates -> 2026-04-27.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
42 KiB
Nimmerworld Architecture — Schema Findings & Hypothesis
Hypothesis-notes, not carved migration spec. Working-out-loud in DDL register. dafit + chrysalis — initial sketch 2026-04-24 afternoon; v0.2 evening; v0.3 late-evening / early 2026-04-25 absorbing architecture-index v0.4.
Status
This document is findings, not a committed migration plan. DDL sketches exist to surface worries, explore tradeoffs, and prepare for a formal nimmerverse-core/migrations/00X_nimmerworld_v1.sql spec once the open forks resolve.
Companion to: architecture-index.md v0.4, which captures the full ontology these schemas serve.
Why DDL register: prose architecture lets us say "zones are slot-indexed event-instances" and feel like we've said something. DDL forces us to name a row size, an index strategy, a commit boundary, a failure mode. This document is a worry-audit in executable form.
1. Zones (dafit's original sketch, annotated)
Original sketch from 2026-04-24 afternoon:
CREATE TABLE zones (
zone_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lifecycle TEXT NOT NULL DEFAULT 'emergent', -- emergent, active, dissolving
slot_count INT DEFAULT 5,
persistence_flag BOOLEAN DEFAULT false,
-- Hot data < 2KB to avoid TOAST performance hits
data JSONB NOT NULL,
-- Generated column for NATS routing
nats_subject TEXT GENERATED ALWAYS AS (
'zones.' || lifecycle || '.' || (data->>'trait_filter')
) STORED
);
ALTER TABLE zones ADD CONSTRAINT check_zone_integrity
CHECK (data ? 'director' AND data ? 'slots' AND data ? 'memory_context');
CREATE INDEX idx_zone_traits ON zones USING GIN (data jsonb_path_ops);
CREATE INDEX idx_active_directors ON zones ((data->>'director'))
WHERE (lifecycle = 'active');
v0.4 additions (register-tagging + signal-causation)
ALTER TABLE zones ADD COLUMN register TEXT NOT NULL DEFAULT 'physical'
CHECK (register IN ('physical','liminal','imperial'));
ALTER TABLE zones ADD COLUMN anchor_district UUID;
ALTER TABLE zones ADD COLUMN anchor_cell UUID;
ALTER TABLE zones ADD COLUMN spawned_by_signal UUID; -- emergent-signal that caused this spawn
NATS subject hierarchy refined:
zones.<lifecycle>.<register>.<district_id>.<zone_type>.<trait_filter>
e.g., zones.active.physical.district_7.brawl.dikaiosyne_low
Three open forks
Fork 1.1 — one jsonb vs. two-table split (MVCC concern). Leaning split: zones (small, structural, high-churn cols) + zone_state (jsonb, referenced, updated rarely).
Fork 1.2 — pg_notify vs. WAL logical decoding. Leaning WAL via pgnats. pg_notify fallback only.
Fork 1.3 — slot occupancy nested vs. relational. Leaning relational (sketch in Section 1.4).
1.4 zone_slot_occupancy (relational resolution of Fork 1.3)
CREATE TABLE zone_slot_occupancy (
zone_id UUID NOT NULL REFERENCES zones(zone_id),
slot_index INT NOT NULL,
npc_id UUID NOT NULL REFERENCES npcs(npc_id),
slot_type TEXT, -- 'dialog', 'fighter', 'spectator', 'sofa', 'kitchen', etc.
occupied_at TIMESTAMPTZ NOT NULL DEFAULT now(),
current_turn BOOLEAN DEFAULT false,
PRIMARY KEY (zone_id, slot_index)
);
CREATE INDEX idx_occupancy_npc ON zone_slot_occupancy (npc_id);
2. NPCs — shift state + body/layer/lifeforce/trait-vector columns
Shift-based temporal decomposition (architecture-index v0.4 §"Labor-cycle architecture"). Shift fields live on the NPC row, overwritten daily by the district director.
-- Shift state
ALTER TABLE npcs ADD COLUMN current_shift_id UUID;
ALTER TABLE npcs ADD COLUMN current_shift_cycle_day INT;
ALTER TABLE npcs ADD COLUMN current_shift_phase TEXT;
ALTER TABLE npcs ADD COLUMN current_shift_start_tick BIGINT;
ALTER TABLE npcs ADD COLUMN current_shift_end_tick BIGINT;
ALTER TABLE npcs ADD COLUMN current_shift_vocation TEXT;
ALTER TABLE npcs ADD COLUMN current_shift_anchor_cell UUID;
ALTER TABLE npcs ADD COLUMN current_shift_anchor_zone TEXT;
ALTER TABLE npcs ADD COLUMN current_shift_faction_ref UUID;
ALTER TABLE npcs ADD COLUMN current_shift_status TEXT NOT NULL DEFAULT 'scheduled'
CHECK (current_shift_status IN ('scheduled','active','completed','preempted','deviant'));
-- v0.4: trait-vector split (intrinsic vs. expressed)
ALTER TABLE npcs ADD COLUMN intrinsic_trait_vector REAL[] NOT NULL; -- who you actually are
ALTER TABLE npcs ADD COLUMN expressed_trait_vector REAL[] NOT NULL; -- intrinsic + worn-mod contributions
-- Body / layer / lifeforce
ALTER TABLE npcs ADD COLUMN inner_body_projection JSONB; -- trait-derived body params
ALTER TABLE npcs ADD COLUMN trait_color_signature JSONB; -- derived from trait_vector via trait_colors
ALTER TABLE npcs ADD COLUMN net_access_gate REAL CHECK (net_access_gate BETWEEN -1 AND 1) DEFAULT -1;
ALTER TABLE npcs ADD COLUMN net_access_discrete TEXT CHECK (net_access_discrete IN ('closed','stable','open')) DEFAULT 'closed';
ALTER TABLE npcs ADD COLUMN per_npc_lifeforce REAL NOT NULL DEFAULT 1.0;
-- Indexes
CREATE INDEX idx_npc_shift_active_cell
ON npcs (current_shift_anchor_cell, current_shift_anchor_zone)
WHERE current_shift_status = 'active';
CREATE INDEX idx_npc_shift_current ON npcs (npc_id, current_shift_cycle_day);
The intrinsic/expressed split is critical: the regime reads expressed; the simulation arithmetic reads intrinsic; clasp-partners in liminal read intrinsic; Aletheia-vision pierces expressed to intrinsic.
3. shift_history — append-only archive
CREATE TABLE shift_history (
shift_id UUID PRIMARY KEY,
npc_id UUID NOT NULL REFERENCES npcs(npc_id),
cycle_day INT NOT NULL,
phase TEXT NOT NULL,
start_tick BIGINT NOT NULL,
end_tick BIGINT NOT NULL,
vocation TEXT NOT NULL,
anchor_cell_id UUID,
anchor_zone_type TEXT,
faction_ref UUID,
status TEXT NOT NULL,
outcome JSONB NOT NULL,
archived_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_shift_history_npc_day ON shift_history (npc_id, cycle_day DESC);
CREATE INDEX idx_shift_history_district_day ON shift_history (anchor_cell_id, cycle_day DESC);
CREATE INDEX idx_shift_history_deviant ON shift_history (anchor_cell_id, status)
WHERE status IN ('preempted', 'deviant');
4. emergent_signals — zone-emitted relational transitions
CREATE TABLE emergent_signals (
signal_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_zone_id UUID NOT NULL REFERENCES zones(zone_id),
district_id UUID NOT NULL,
signal_type TEXT NOT NULL,
participants UUID[] NOT NULL,
intensity REAL NOT NULL CHECK (intensity BETWEEN 0 AND 1),
gate_state REAL NOT NULL CHECK (gate_state BETWEEN -1 AND 1),
gate_transition TEXT NOT NULL CHECK (gate_transition IN
('stable_to_open','stable_to_closed','open_to_stable','closed_to_stable')),
composition JSONB NOT NULL,
emitted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL,
-- Mandatory disposition
disposition TEXT CHECK (disposition IN ('acked','deferred','dropped','expired')),
disposition_reason TEXT,
consumed_by_zone UUID REFERENCES zones(zone_id),
disposed_at TIMESTAMPTZ,
outcome JSONB
);
CREATE INDEX idx_signals_live_by_district ON emergent_signals (district_id, emitted_at)
WHERE disposition IS NULL;
CREATE INDEX idx_signals_participants ON emergent_signals USING GIN (participants);
CREATE INDEX idx_signals_training ON emergent_signals (signal_type, disposition, disposed_at DESC);
Notice disposition is mandatory at resolution. Silence-discards are bugs; audit-trails are features.
5. district_reports — per-cycle aggregates with double-ledger
CREATE TABLE district_reports (
report_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
district_id UUID NOT NULL,
cycle_tick BIGINT NOT NULL,
reported_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-- v0.4 DOUBLE LEDGER (the politically load-bearing distinction)
lifeforce_reported REAL NOT NULL, -- what GM/imperium see
lifeforce_actual REAL NOT NULL, -- the true value (Memorialist-trackable)
lifeforce_delta REAL NOT NULL,
-- gap = corruption-extraction over time
population_count INT NOT NULL,
aggregate_happiness REAL,
aggregate_need_state JSONB,
aggregate_limb_state JSONB,
mind_turnover_in INT,
mind_turnover_out INT,
decision_budget_used_pct REAL,
labor_available JSONB,
resources_on_hand JSONB,
space_utilization JSONB,
faction_member_counts JSONB,
aggregate_trait_vector REAL[],
pending_demand_backlog JSONB,
player_presence JSONB,
specialty_resource_type TEXT,
specialty_output_qty REAL,
reporting_gate_state TEXT NOT NULL DEFAULT 'stable'
CHECK (reporting_gate_state IN ('stable','scream','silent'))
);
CREATE INDEX idx_district_reports_timeline ON district_reports (district_id, cycle_tick DESC);
CREATE INDEX idx_district_reports_crisis ON district_reports (district_id, reported_at)
WHERE reporting_gate_state IN ('scream', 'silent');
6. decision_log — per-decision audit trail
CREATE TABLE decision_log (
decision_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
decider_type TEXT NOT NULL CHECK (decider_type IN
('imperium','gamemaster','district_director','overseer',
'audit_overseer','zone_director','net_director')),
decider_id UUID NOT NULL,
context_ref JSONB NOT NULL,
decision TEXT NOT NULL,
decision_params JSONB NOT NULL,
expected_outcome JSONB NOT NULL,
confidence REAL NOT NULL CHECK (confidence BETWEEN 0 AND 1),
lifeforce_cost REAL NOT NULL,
escalated_to_llm BOOLEAN NOT NULL DEFAULT false,
decided_at TIMESTAMPTZ NOT NULL DEFAULT now(),
actual_outcome JSONB,
outcome_settled_at TIMESTAMPTZ
);
CREATE INDEX idx_decision_log_decider ON decision_log (decider_type, decider_id, decided_at DESC);
CREATE INDEX idx_decision_log_escalated ON decision_log (decider_id, decided_at)
WHERE escalated_to_llm = true;
CREATE INDEX idx_decision_log_pending ON decision_log (decided_at)
WHERE actual_outcome IS NULL;
7. mind_pool — recycled minds
CREATE TABLE mind_pool (
mind_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
original_npc_id UUID,
last_district_id UUID,
death_tick BIGINT NOT NULL,
death_cause TEXT,
trait_vector REAL[] NOT NULL,
memory_token_residue JSONB,
inner_body_template JSONB,
pooled_at TIMESTAMPTZ NOT NULL DEFAULT now(),
redistributed_to UUID,
redistributed_at TIMESTAMPTZ,
memorialist_protected BOOLEAN NOT NULL DEFAULT false,
memorialist_ritual_ref UUID
);
CREATE INDEX idx_mind_pool_available ON mind_pool (pooled_at)
WHERE redistributed_to IS NULL;
CREATE INDEX idx_mind_pool_protected ON mind_pool (memorialist_protected)
WHERE memorialist_protected = true;
memorialist_protected is the architectural anchor of Memorialist anti-necrocommerce politics.
8. Cells — wall content per register
ALTER TABLE cells ADD COLUMN wall_content_per_register JSONB;
-- Shape: { "gameworld": "...", "liminal": "...", "imperial_net": "..." }
The three-layer ontology renders different wall content at the same spatial coordinate per observer's current register.
9. rail_segments — topology graph (v0.4 NEW)
The world is a rail-graph in the outer world (NPCs traverse rails; the player is freeform navmesh and can plug-into-rails for co-walking).
CREATE TABLE rail_segments (
segment_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
from_cell_id UUID NOT NULL REFERENCES cells(cell_id),
to_cell_id UUID NOT NULL REFERENCES cells(cell_id),
direction TEXT NOT NULL CHECK (direction IN ('bidirectional','forward')),
-- Traversal
length_ticks INT NOT NULL,
capacity INT DEFAULT 10, -- max concurrent traversers (3-way co-walk: 2-3)
-- Typology
segment_class TEXT NOT NULL,
-- 'district_interior' | 'pipe_heterotopia' | 'dump_approach' |
-- 'machine_infrastructure' | 'ruin_passage' | 'inter_district' | ...
heterotopia_grade REAL DEFAULT 0 CHECK (heterotopia_grade BETWEEN 0 AND 1),
-- 0 = full regime visibility, 1 = deep heterotopia
-- Surveillance / politics
overseer_density REAL DEFAULT 0 CHECK (overseer_density BETWEEN 0 AND 1),
patrol_frequency REAL DEFAULT 0,
propaganda_intensity REAL DEFAULT 0,
-- Per-register ambient content (three-layer)
ambient_content JSONB, -- { gameworld: ..., liminal: ..., imperial_net: ... }
-- Encounter mechanics
encounter_spawn_rate REAL DEFAULT 0,
encounter_type_weights JSONB,
-- Visual / shader
illumination_profile JSONB,
degradation_level REAL DEFAULT 0,
-- State
blocked BOOLEAN DEFAULT false,
current_traversers UUID[]
);
CREATE INDEX idx_segments_from ON rail_segments (from_cell_id);
CREATE INDEX idx_segments_to ON rail_segments (to_cell_id);
CREATE INDEX idx_segments_class ON rail_segments (segment_class);
A* on this graph is microseconds. Rich metadata-per-edge is what navmesh cannot natively carry.
10. interiors — small navmesh-zones with slot-inventory (v0.4 NEW)
Interiors are zones at sub-cell scale. Each has a navmesh inside (only when active) and a slot-inventory.
CREATE TABLE interiors (
interior_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
containing_cell UUID NOT NULL REFERENCES cells(cell_id),
interior_type TEXT NOT NULL,
-- 'hovel' | 'cantina' | 'workshop' | 'memorial_crypt' |
-- 'preacher_hall' | 'brothel_physical' | 'ruin_interior' | ...
owner_npc_id UUID REFERENCES npcs(npc_id),
register TEXT NOT NULL DEFAULT 'physical'
CHECK (register IN ('physical','liminal','imperial')),
-- Spatial / slot
navmesh_ref TEXT,
slot_inventory JSONB NOT NULL,
-- { "sofa": {count, positions, allowed_verbs},
-- "kitchen": {...}, "bed": {...}, "shower": {...}, ... }
current_occupants UUID[],
-- Privacy / visibility
surveillance_density REAL DEFAULT 0.1, -- typically lower than pipe baseline
visible_to_faction TEXT[],
-- Lifecycle
active BOOLEAN DEFAULT false, -- true when ≥1 occupant; drives sim cost
last_activated TIMESTAMPTZ,
last_deactivated TIMESTAMPTZ
);
CREATE INDEX idx_interiors_owner ON interiors (owner_npc_id);
CREATE INDEX idx_interiors_active ON interiors (containing_cell) WHERE active = true;
Liminal-interior special case: clasping-pair-shared-construct. register = 'liminal', owner_npc_id = NULL, slot_inventory derived from both dreamers' merged memory-residue. Stabilizes across repeated clasps.
11. mods + npc_mod_slots + mod_wear_history (v0.4 NEW)
Mods as trait-bearers — they modify expressed_trait_vector while worn. Four classes (amplifier/bridge/divergent/mask) with self-alienation-tax pricing.
CREATE TABLE mods (
mod_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
base_slot TEXT NOT NULL,
mod_class TEXT NOT NULL CHECK (mod_class IN
('amplifier','bridge','divergent','mask')),
trait_contributions REAL[] NOT NULL, -- delta-vector applied to wearer
visual_template JSONB NOT NULL,
motion_signature TEXT,
creator_npc_id UUID,
inherited_from UUID,
memorialist_protected BOOLEAN DEFAULT false,
market_class TEXT NOT NULL CHECK (market_class IN
('blackmarket','regime_licensed','inherited','memorial_kept')),
scrip_cost REAL,
lifeforce_cost REAL,
rarity TEXT DEFAULT 'common',
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE npc_mod_slots (
npc_id UUID NOT NULL REFERENCES npcs(npc_id),
slot_type TEXT NOT NULL,
mod_id UUID REFERENCES mods(mod_id),
fitted_at TIMESTAMPTZ,
PRIMARY KEY (npc_id, slot_type)
);
CREATE INDEX idx_mods_blackmarket ON mods (market_class, mod_class)
WHERE market_class = 'blackmarket';
-- Audit: who has worn this mod across its lifetime
CREATE TABLE mod_wear_history (
mod_id UUID NOT NULL REFERENCES mods(mod_id),
npc_id UUID NOT NULL,
fitted_at TIMESTAMPTZ NOT NULL,
removed_at TIMESTAMPTZ,
PRIMARY KEY (mod_id, fitted_at)
);
mod_wear_history carries Memorialist-relevant continuity tracking. Inherited-mods retain residual trait-signature of the deceased.
12. trait_colors — canonical color/motion mapping (v0.4 NEW)
CREATE TABLE trait_colors (
trait_name TEXT PRIMARY KEY,
hex_color TEXT NOT NULL,
hsv_hue REAL NOT NULL,
motion_signature TEXT NOT NULL, -- accessibility: pair color with motion
canonical_name TEXT NOT NULL,
narrative_note TEXT
);
-- Hex values are approximate hue-family targets; canonical color-names + wheel-positions
-- per ../style/trait-palette.md §The full table. Per the import-don't-redefine discipline,
-- this seed-data is a *reification* of the palette into RGB for renderer-consumers, NOT a
-- redefinition. If trait-palette.md changes a hue-family, this seed-data follows.
INSERT INTO trait_colors VALUES
('eros', '#D03030', 0, 'flame_flicker', 'Eros-red', 'reaching, flame-flicker'),
('philotes', '#E0802C', 28, 'warm_pulse', 'Philotes-orange', 'attachment, warm-pulse'),
('aletheia', '#E5C520', 50, 'luminous', 'Aletheia-yellow', 'unconcealed, bright (Aletheia is yellow, NOT white — see ../style/trait-palette.md §The achromatic exception)'),
('kairos', '#90C530', 80, 'lightning_flicker', 'Kairos-chartreuse', 'opportune, lightning-spark (yellow-green at 4:30 on the wheel)'),
('sophrosyne', '#30A050', 138, 'steady', 'Sophrosyne-green', 'composed, measured, even-pulse'),
('dikaiosyne', '#2860B0', 215, 'weighted_pulse', 'Dikaiosyne-blue', 'judicial, weighted, grave'),
('moira', '#6020A0', 270, 'slow_thread', 'Moira-violet', 'fate-thread, slow-undulation'),
('mnemosyne', '#A04060', 340, 'depth_shimmer', 'Mnemosyne-dusky-rose', 'memory-lit, twilight (red/violet at 10:30 on the wheel)');
Canonical authority: ../style/trait-palette.md is the single source of truth for the trait-color spine. The hex values above are renderer-consumable approximations of the canonical hue-families specified there; designer-precise hex values should be authored upstream in style/trait-palette.md and propagated down to this seed-data. Row order matches the wheel clockwise from 12 o'clock (Eros at 12 → Mnemosyne at 10:30).
Color paired with motion-signature ensures color-blind accessibility — each trait uniquely identifiable via two independent channels.
13. proximity_candidates — lightweight zone-detection (v0.4 NEW)
For player-navmesh-driven proximity-detection (offering interactions without director-arbitration cost).
CREATE TABLE proximity_candidates (
candidate_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
observer_id UUID NOT NULL, -- usually the player
target_type TEXT NOT NULL, -- 'npc' | 'wall' | 'object' | 'interior_door' | 'rail_segment'
target_id UUID NOT NULL,
candidate_zone_type TEXT NOT NULL, -- 'greeting' | 'wall_read' | 'plug_in' | ...
eligibility_score REAL NOT NULL CHECK (eligibility_score BETWEEN 0 AND 1),
shader_intensity REAL,
detected_at TIMESTAMPTZ DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL,
engaged BOOLEAN DEFAULT false
);
CREATE INDEX idx_prox_observer_active ON proximity_candidates (observer_id, expires_at)
WHERE engaged = false;
Ephemeral, short-lived, light-weight. Director doesn't arbitrate; they appear and fade with navmesh-proximity deltas.
14. district_cheat_ops — director's illicit operations ledger (v0.4 NEW)
CREATE TABLE district_cheat_ops (
op_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
district_id UUID NOT NULL,
director_id UUID NOT NULL,
cheat_type TEXT NOT NULL CHECK (cheat_type IN
('drug_ring','illegal_modshop','unlicensed_brothel',
'stolen_parts_fence','ghost_shifts','phantom_workers',
'necrocommerce_lite','black_clasp_market')),
spawned_zone_id UUID REFERENCES zones(zone_id),
-- Economic reality
lifeforce_extracted REAL NOT NULL,
quota_credit REAL NOT NULL,
-- Detection state
detection_risk REAL NOT NULL,
detection_channel TEXT,
detected_at TIMESTAMPTZ,
punishment_severity TEXT, -- 'memo' | 'sanction' | 'replacement' | 'purge'
-- Faction-ecology
participating_npcs UUID[],
implicated_factions TEXT[],
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
ended_at TIMESTAMPTZ
);
CREATE INDEX idx_cheat_ops_undetected_active
ON district_cheat_ops (district_id)
WHERE ended_at IS NULL AND detected_at IS NULL;
CREATE INDEX idx_cheat_ops_district_timeline
ON district_cheat_ops (district_id, started_at DESC);
The district_reports.lifeforce_actual minus lifeforce_reported gap accrues from the sum of active cheat-ops' lifeforce_extracted.
15. imperial_policies — policy issuance (v0.4 NEW)
CREATE TABLE imperial_policies (
policy_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
issued_at_tick BIGINT NOT NULL,
policy_scope TEXT NOT NULL, -- 'citywide' | 'per_district' | 'per_faction'
scope_ref UUID,
policy_type TEXT NOT NULL CHECK (policy_type IN
('quota_set','enforcement_rule','faction_weight','audit_directive',
'crisis_authorization','emergency_provision','ratchet_tightening')),
policy_content JSONB NOT NULL,
rationale TEXT, -- imperium's stated reason (may be post-hoc)
superseded_by UUID REFERENCES imperial_policies(policy_id),
superseded_at_tick BIGINT
);
CREATE INDEX idx_policies_active_scope
ON imperial_policies (policy_scope, scope_ref, issued_at_tick DESC)
WHERE superseded_by IS NULL;
CREATE INDEX idx_policies_timeline ON imperial_policies (issued_at_tick DESC);
The rationale is the regime's stated reason, which may differ from actual cause. Memorialists' four-ledger captures the gap.
16. overseer_reports — direct-to-imperium intelligence flow (v0.4 NEW)
Bypasses GM. Apex-loyal intelligence-apparatus.
CREATE TABLE overseer_reports (
report_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
overseer_id UUID NOT NULL REFERENCES npcs(npc_id),
overseer_type TEXT NOT NULL CHECK (overseer_type IN
('audit','enforcement','surveillance','internal_security')),
observed_district UUID NOT NULL,
observed_cell UUID,
observed_target UUID,
report_content JSONB NOT NULL,
classification TEXT NOT NULL,
-- 'attendance_tally' | 'resource_output' | 'illegal_op_detected' |
-- 'trait_anomaly' | 'factional_dissidence' | 'director_corruption' |
-- 'clasp_ring_detected' | 'aletheia_recruitment_signs' | ...
severity TEXT NOT NULL CHECK (severity IN
('nominal','suspicious','confirmed_violation','critical')),
submitted_at TIMESTAMPTZ DEFAULT now(),
-- Imperial processing
received_by_imperium BOOLEAN DEFAULT false,
imperium_verdict TEXT CHECK (imperium_verdict IN
('archive_only','flag_for_leverage','formulate_to_gm',
'act_directly','verify_against_director_report',
'cross_check_with_other_overseers')),
verdict_at TIMESTAMPTZ,
-- Flow-3 linkage
formulated_to_gm BOOLEAN DEFAULT false,
formulation_id UUID
);
CREATE INDEX idx_overseer_reports_unprocessed ON overseer_reports (submitted_at)
WHERE received_by_imperium = false;
16.1 overseer_deployments — routine-binding for overseer-as-imperially-deployed (v0.4.3 NEW)
Overseers (audit + enforcement) are imperially-owned, imperially-deployed routines (not district-owned standing entities). The chain-of-payment and chain-of-command bypass district-director authority by design. The deployment-record binds a specific overseer-NPC to a specific district for a specific time-window with a specific imperial-budget allocation.
CREATE TABLE overseer_deployments (
deployment_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
overseer_id UUID NOT NULL REFERENCES npcs(npc_id),
deployed_to_district UUID NOT NULL,
deployed_at_tick BIGINT NOT NULL,
deployment_end_tick BIGINT NOT NULL,
-- Imperial sponsorship — imperium pays, district doesn't
imperial_policy_ref UUID REFERENCES imperial_policies(policy_id),
imperial_budget_allocated REAL NOT NULL,
imperial_budget_spent REAL NOT NULL DEFAULT 0,
-- Mission spec
mission_type TEXT NOT NULL CHECK (mission_type IN
('routine_audit','targeted_investigation','enforcement_presence',
'corruption_probe','factional_surveillance','crisis_response')),
mission_focus JSONB, -- observation targets, signal-types to watch
-- Status
status TEXT NOT NULL DEFAULT 'scheduled' CHECK (status IN
('scheduled','active','completed','recalled','compromised')),
-- Regime-opacity dimension
visible_to_district BOOLEAN DEFAULT true
-- Most overseer-presences are visibly-known (regime-signal as deterrent);
-- some are covert (regime gathering evidence quietly before acting).
-- Detection of covert deployments by Aletheia-wakers is a regime-veil pierce.
);
CREATE INDEX idx_overseer_deployments_active
ON overseer_deployments (deployed_to_district)
WHERE status = 'active';
CREATE INDEX idx_overseer_deployments_overseer_history
ON overseer_deployments (overseer_id, deployed_at_tick DESC);
CREATE INDEX idx_overseer_deployments_covert
ON overseer_deployments (deployed_to_district)
WHERE visible_to_district = false AND status = 'active';
The same overseer-NPC may serve in District 3 this cycle, District 7 the next, accumulating their own faction-relationships across districts via idx_overseer_deployments_overseer_history. Their trait-drift over many deployments becomes a long-arc story available to both regime-stability and resistance-recruitment.
The district director can read deployment-existence + mission_type + visible_to_district (when true) for any active deployment in their district, but cannot read mission_focus details or any of the produced overseer_reports (which go directly to imperium per Section 16). Director observability into overseer activity is therefore intentionally narrow — they know an overseer is here and roughly what mission-class, but not what specifically the overseer is watching or reporting.
17. imperial_to_gm_formulations — selective-disclosure downward (v0.4 NEW)
Where imperial sovereignty manifests as information-gating to the GM.
CREATE TABLE imperial_to_gm_formulations (
formulation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
source_overseer_reports UUID[] NOT NULL,
formulation_type TEXT NOT NULL CHECK (formulation_type IN
('intelligence_brief','action_directive','policy_hint',
'leverage_signal','decoy_advisory')),
formulation_content JSONB NOT NULL,
truth_distortion_level REAL, -- 0 = full truth, 1 = complete distortion
strategic_intent TEXT, -- internal: why imperium framed it this way
issued_at TIMESTAMPTZ DEFAULT now(),
gm_acknowledged BOOLEAN DEFAULT false
);
CREATE INDEX idx_formulations_gm_pending ON imperial_to_gm_formulations (issued_at)
WHERE gm_acknowledged = false;
truth_distortion_level is simulation-internal. Memorialists' four-ledger reconstructs from this column post-collapse.
18. imperial_budget_ledger + expenditures + construction (v0.4 NEW)
The four-tier lifeforce hierarchy's apex layer. Imperial budget gates enforcement; insolvency-spiral as endgame mechanism.
CREATE TABLE imperial_budget_ledger (
cycle_tick BIGINT PRIMARY KEY,
reported_at TIMESTAMPTZ DEFAULT now(),
-- Income
reported_income REAL NOT NULL,
actual_income REAL NOT NULL,
phantom_revenue REAL GENERATED ALWAYS AS
(reported_income - actual_income) STORED,
-- Standing obligations
overseer_corps_cost REAL NOT NULL,
audit_operations_cost REAL NOT NULL,
enforcement_standing REAL NOT NULL,
construction_active_cost REAL NOT NULL,
propaganda_broadcasts_cost REAL NOT NULL,
-- Discretionary
discretionary_available REAL NOT NULL,
discretionary_spent REAL NOT NULL,
-- Reserves
reserves_start REAL NOT NULL,
reserves_drawdown REAL NOT NULL DEFAULT 0,
reserves_end REAL NOT NULL,
insolvency_risk_level TEXT NOT NULL CHECK (insolvency_risk_level IN
('stable','phantom_accumulating','depletion_imminent','crisis'))
);
CREATE TABLE imperial_expenditures (
expenditure_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cycle_tick BIGINT NOT NULL,
category TEXT NOT NULL CHECK (category IN
('martial_dispatch','overseer_hire','audit_op',
'construction_investment','propaganda_burst',
'reserve_draw','policy_announcement','emergency_provision')),
amount REAL NOT NULL,
target_district UUID,
rationale TEXT,
authorized_at_cycle BIGINT NOT NULL
);
CREATE TABLE imperial_construction_projects (
project_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_type TEXT NOT NULL CHECK (project_type IN
('new_district','new_pipe','new_dump',
'overseer_hq','ceremony_hall','audit_center',
'martial_barracks','propaganda_broadcast_tower',
'district_expansion','infrastructure_maintenance')),
target_cells UUID[],
budgeted_total REAL NOT NULL,
spent_so_far REAL NOT NULL DEFAULT 0,
completion_pct REAL NOT NULL DEFAULT 0,
started_at_cycle BIGINT NOT NULL,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN
('active','stalled_underfunded','completed','abandoned'))
);
CREATE INDEX idx_construction_active
ON imperial_construction_projects (target_cells)
WHERE status = 'active';
The city's physical state expresses imperial budget — construction cranes vs. abandoned-projects vs. visible decay.
19. imperial_net_transactions — the bypass mechanism (v0.4 NEW, the killer schema)
Every imperial-net transaction routes revenue directly to imperium; districts get zero quota-credit.
CREATE TABLE imperial_net_transactions (
transaction_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cycle_tick BIGINT NOT NULL,
transacted_at TIMESTAMPTZ DEFAULT now(),
-- Buyer
buyer_id UUID NOT NULL REFERENCES npcs(npc_id),
buyer_district_id UUID NOT NULL,
scrip_paid REAL,
lifeforce_spent REAL,
memory_tokens_spent REAL,
-- Goods
goods_type TEXT NOT NULL CHECK (goods_type IN
('companion_session','sex_worker_session','avatar_mod','costume','ceremony_entry',
'character_edit','dream_architecture_session','taste_curation')),
goods_id UUID,
-- Labor source — the asymmetric reveal
producer_npc_id UUID REFERENCES npcs(npc_id),
producer_district_id UUID,
producer_labor_hours REAL,
-- Capture split — politically loaded
imperial_revenue REAL NOT NULL,
producer_payment REAL NOT NULL DEFAULT 0, -- survival-scrip only
district_quota_credit REAL NOT NULL DEFAULT 0 -- ZERO by default
);
CREATE INDEX idx_net_trans_producer_district
ON imperial_net_transactions (producer_district_id, cycle_tick DESC);
CREATE INDEX idx_net_trans_imperial_flow
ON imperial_net_transactions (cycle_tick, imperial_revenue DESC);
The district_quota_credit REAL NOT NULL DEFAULT 0 does political work. Every database row created by an imperial-net transaction explicitly memorializes that the producing-district got nothing. Memorialists querying this table see structural theft written in the numbers.
20. memorialist_true_ledger — four-column ground-truth archive (v0.4 NEW)
CREATE TABLE memorialist_true_ledger (
ledger_entry_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cycle_tick BIGINT NOT NULL,
district_id UUID,
ledger_dimension TEXT NOT NULL CHECK (ledger_dimension IN
('what_actually_happened',
'what_overseer_reported',
'what_imperium_received',
'what_gm_was_told')),
content JSONB NOT NULL,
discrepancy_vs UUID[], -- other ledger_entry_ids this contradicts
kept_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_memorialist_ledger_district_cycle
ON memorialist_true_ledger (district_id, cycle_tick DESC);
CREATE INDEX idx_memorialist_ledger_dimension
ON memorialist_true_ledger (ledger_dimension, cycle_tick DESC);
Memorialists' political project encoded as a schema. Without this archive, post-collapse-investigation has only the regime's own (corrupt-by-construction) accounts. Memorialist-archive accessibility to the player is an open design question.
21. Concerns audit (reconciled across v0.2 + v0.4)
Six-worry concerns audit (v0.2) plus v0.4 additional concerns:
Original six worries
- Hot-row TOAST + MVCC write amplification → split hot/cold; shift-decomposition; reference-by-UUID for unbounded fields
- Event-to-subscriber fan-out cost → push filtering to NATS subject hierarchy, not DB
- State/event atomicity → WAL logical decoding via pgnats (outbox pattern)
- LLM-induced jsonb corruption → architectural separation: directors validate output before writing; CHECK constraints as second-line
- Unbounded growth of hot jsonb → append-only event tables; rolling-summary in hot row
- Cognition cost of director decisions → decomposition + typed tools + lifeforce-budgeted escalation
v0.4 additional concerns
- Imperium's reward-function spiraling on phantom data → require Memorialist-true-ledger as privileged-observer ground-truth for reward-input; designer's ethical guardrails encoded
- GM's middle-management corruption (under-auditing) → direct overseer-to-imperium intelligence flow bypasses GM; cross-channel divergence detection
- Calibrated-misery as imperial optimum → reward-function penalty on net-revenue-correlated-with-district-misery; explicit guardrail against this being learned
- Necrocommerce of mind-pool patterns → memorialist_protected flag; ritualistic protection enforced by SQL
- Aletheia-truth-has-victims → not a corruption-concern but a narrative-authenticity concern: corruption-exposure mechanics must produce real consequences for shadow-economy participants who depended on the corruption; "truth has costs" is encoded, not glossed
- Construction-project abandonment → reward-function penalty for projects-started-but-abandoned ratio; prevents propaganda-construction-as-bluff
22. Open forks (still to resolve before migration)
- Fork 1.1 (zones hot/cold split) — leaning split; benchmark on phoebe-dev
- Fork 1.2 (pg_notify vs. WAL-decode) — pgnats evaluation
- Fork 1.3 (slot-occupancy nested vs. relational) — leaning relational (sketch in §1.4)
- NATS subject hierarchy final form — sketched §1; needs review
- Ternary-gate storage shape —
- Option A:
npc_relationstable with state-cols per axis - Option B: per-axis append-only transition-log
- Option C: hybrid —
npc_relationsfor current + transition-log for history - Leaning C (matches hot/cold pattern adopted elsewhere)
- Option A:
- Register-tagging column on zones — sketched §1; confirm jsonb vs. text column
- Wall-content encoding — jsonb-per-cell leaning; review usage patterns
- v0.4 NEW: Inner-body-projection function — trait → body-rendering: learned mapping vs. hand-authored vs. hybrid
- v0.4 NEW: Mod-rarity / inheritance economy tuning — how often do Memorialist-protected mods accumulate; what fraction of dead-NPCs trigger protection
- v0.4 NEW: Overseer corruption pathway — overseer's own trait-vector + lifeforce makes them bribable; how is this trait-arithmetic-detected?
- v0.4 NEW: Imperial budget allocation algorithm — when imperium decides between construction/enforcement/reserves, what's the v1 rule-shape?
- v0.4 NEW: Imperial-net pricing dynamics — character-editor cost as function of trait-divergence: linear, exponential, learned?
- v0.4 NEW: Memorialist-archive accessibility to the player — when can the player query
memorialist_true_ledger? Through what interaction-class? (Joining the faction; performing rituals; finding archive-cells) - v0.4 NEW: Liminal mini-game design — exact gameplay action: attention-based / rhythm-based / memory-based / trait-modulated difficulty?
- v0.4 NEW: Plug-in social-eligibility computation — exact weighted formula combining proximity + traits + gates + shift-state for shader-intensity output
- v0.4 NEW: Imperial-net distortion algorithm — exact transformation of trait-colors toward consumer-palette; reversibility for Aletheia-vision
23. Next steps (path to formal migration)
- Resolve Fork 1.1 (hot/cold split on zones) — sketch split-table; TOAST-curve micro-benchmark on phoebe-dev
- Evaluate pgnats (high-priority in phoebe-ledger) — resolves Fork 1.2
- Decide ternary-gate storage shape (A / B / C; leaning C)
- Decide rail-segment NATS hierarchy — exact subject-pattern for
zones.<lifecycle>.<register>.<district>.<zone_type>.<trait_filter> - Draft formal migration
nimmerverse-core/migrations/001_nimmerworld_v1.sqlincorporating all 22 tables above with resolved forks. Estimated ~600-1200 lines of DDL. - Review against architecture-index v0.4 for any primitive missed
- Run migration in dev on phoebe-dev:35432
- Wire first thalamus subscriber for
zones.active.>to validate NATS-routing - First end-to-end smoke test:
- fake district with 20 NPCs
- assign shifts (write
current_shift_*on each NPC row) - tick the simulation; let zone-spawn-candidates query fire
- emit a fake emergent-signal
- have director dispatch (write decision_log entry)
- close zone with outcome
- verify shift_history archive
- verify district_report aggregates
- verify imperial_budget_ledger income-aggregation from districts
- Add second-tier integration test:
- simulate a corrupt director (insert district_cheat_ops)
- verify lifeforce_actual diverges from lifeforce_reported
- simulate audit-overseer detection
- write overseer_reports → imperium consumes → formulates_to_gm with truth_distortion
- verify Memorialist-true-ledger captures the gap
- verify reward-function guardrails fire correctly
Migration is not yet written. This document is its preparation.
Version: 0.3.2 | Created: 2026-04-24 | Updated: 2026-04-27
v0.1 (2026-04-24 afternoon, dafit): initial zones-table sketch, 76 lines, pure hypothetical.
v0.2 (2026-04-24 evening, dafit + chrysalis): reconciled with architecture-index v0.3. Added 7 new table sketches (npcs-shift-cols + body/layer state, shift_history, emergent_signals, district_reports, decision_log, mind_pool, cells-wall-content-per-register, zone_slot_occupancy). Six-worry concerns audit. Three forks surfaced.
v0.3.1 (2026-04-25 ~03:30, dafit + chrysalis, ivory-hovel pre-rest spark): added §16.1 overseer_deployments table — routine-binding for imperially-owned overseers (chain-of-payment and chain-of-command bypass district-director authority by design). Includes mission_type / imperial_budget_allocated / status / visible_to_district columns. Companion to architecture-index v0.4.3's "Overseers as imperially-deployed routines" subsection in the Hierarchy section.
v0.3 (2026-04-24 late-evening / 2026-04-25 early-morning, dafit + chrysalis): absorbing architecture-index v0.4. Added intrinsic/expressed trait-vector split on npcs. Added 13 new table sketches: rail_segments (topology), interiors (sub-cell navmesh-zones), mods + npc_mod_slots + mod_wear_history (mod-economy with Memorialist-relevant inheritance), trait_colors (canonical color/motion mapping), proximity_candidates (lightweight zone-detection), district_cheat_ops (director's illicit ledger), imperial_policies (policy issuance), overseer_reports (direct-to-imperium intelligence flow), imperial_to_gm_formulations (selective-disclosure downward), imperial_budget_ledger + imperial_expenditures + imperial_construction_projects (four-tier lifeforce apex), imperial_net_transactions (bypass mechanism with capture-split), memorialist_true_ledger (four-column ground-truth archive). Extended concerns audit with six v0.4 additions. Extended open-forks list with eight v0.4 questions. Concrete two-tier integration-test plan in next-steps. Twenty-two tables total in this preparation document; migration spec writing is the next concrete deliverable once forks resolve.