Bang Import Pipeline¶
Status: 🚧 Partial — The bang import pipeline is the REAL live generation path (BangImportService invokes the bang Docker sidecar, parses Universe JSON … (Re-verified 2026-08-21 vs origin/feat
46bce720.) Tip delta from prior5e198fa5stamp is GS #709 (accept/redact/block moderation) — no bang-import inventory change. Re-corrected 2026-08-06 (supersedes the 2026-08-04 correction, now stale): the § Canonicality contract's gameserver-side validation gate (galaxy_validation.py,ERR_BANG_VALIDATION_FAILED) shipped 2026-08-05 and is wired intobang_import_service.py's import flow — but only checks structural/referential integrity, not bang's per-formation placement invariants (still bang-side-only enforcement) or content-layer counts. See § Canonicality contract for the precise coverage boundary.Launch-target architecture, partially extracted. This pipeline is the launch design: galaxy generation is owned by a separate
sw2102-bangservice that emitsUniverseJSON, and the gameserver imports that JSON via the pipeline below.BangImportService(services/gameserver/src/services/bang_import_service.py, 3,100+ lines) is real and wired into 9 endpoints onapi/routes/bang_galaxy.py, plus call sites across the sector/nebula/quantum services. The translator code paths cited below (bang_schema.py,bootstrap_from_bang.py,commodity_catalog.py, etc.) describe where the remaining, not-yet-split-out pieces of that surface should live. See./galaxy-generation.mdfor the live in-process generator.
Purpose¶
The bang import pipeline translates a sw2102-bang Universe JSON (or its equivalent rows in the bang PostgreSQL schema) into a fully-scaffolded gameserver multi-region galaxy. It is the canonical bridge between the world generator and the live game: every path that materializes generator output into the gameserver — admin-triggered import, first-time bootstrap, Alembic data fixture — invokes this pipeline. The pipeline is deterministic for a given input and target-region context, idempotent on re-run, and writes its output in a single transaction so a partial failure never leaves the gameserver schema in a half-imported state.
Worldgen-content allow-list (runtime content rejected)¶
Per ADR-0053 WR5 and ADR-0069, the import pipeline accepts only worldgen-content tables. Runtime-content tables in the payload are rejected.
Worldgen-importable (allow-list):
Region(top-level metadata for the region being imported)SectorCluster(named, sector-range-bounded; faction-influence values seeded gameserver-side)ZoneStation(the structure plus initial inventory; not its market-transaction state)Planet(including initial inventory and citadel content)WarpTunnelsector_warpsSpecialFormationNPCRoster(count + host-sector entries;NPCCharacterrows materialize at runtime)
Runtime-rejected (deny-list — presence triggers ERR_BANG_IMPORT_INCLUDES_RUNTIME_CONTENT listing the offending tables):
MessageBeaconNPCCharacter,NPCDeathLogMarketTransaction(enhanced_market_transactions),MarketPrice,PriceHistoryCombatLog,CombatStatsBountyClaim,BountyHunterMembershipARIAObservationLog,ARIAPersonalMemory,ARIAMarketIntelligence,ARIAExplorationMap,ARIAQuantumCachePirateKillLog,PirateHoldingOutlawBase(lodging structure is runtime-only; worldgen-time pre-seeding in Phase 12.6 creates these via the gameserver, not via bang import)Player,User,PlayerCentralBankAccount,Reputation,TeamMembership- All other audit-trail tables
The pipeline validates both presence (every worldgen table is complete and consistent — the shape-only check per the canonicality contract below; per-formation invariant re-validation is design-only, not built) and absence (no runtime-table rows in the payload). Rejection lists every offending table so the bang-side debugger can identify which tables were inadvertently included.
Canonicality contract¶
Per SK20 in ADR-0050 and ADR-0069, the gameserver is canonical for runtime behavior — this is the launch-target design. Re-corrected 2026-08-06 (supersedes the 2026-08-04 correction below, now stale in the opposite direction): services/gameserver/src/services/galaxy_validation.py does now exist and is wired (WO-build-adr0050-sk20-bang-canonicality, shipped 2026-08-05) — bang_import_service.py calls validate_insert_plan_or_raise/validate_region_plan_or_raise at 3 sites and raises ERR_BANG_VALIDATION_FAILED on failure. It is narrower than ADR-0050's full aspiration, though: it checks structural/referential integrity only (sector count + contiguity, capital-sector uniqueness, warp/cluster/station/planet/formation referential integrity) — it does not re-validate bang's per-formation semantic invariants (e.g. ADR-0046's WARP_SINK/BUBBLE placement rule) or content-layer invariants (one TERRA at Capital, two SpaceDocks), and per the module's own documented coverage gap, NPC-roster and station/planet inventory content also aren't checked. The design intent below is preserved as the launch target; items 3-4 are now shipped-but-narrower rather than design-only:
- Bang produces a Universe JSON dataset — clusters, special formations, station/planet inventories, citadel content, and NPC rosters all included.
- Bang internally enforces the same invariants Phase 13 was designed to enforce (notably ADR-0046's "no
WARP_SINKinside aBUBBLE") so output is correct-by-construction — this part is real and live, and remains the only enforcement of formation-placement/content-layer invariants (the gameserver gate below doesn't re-check them). - ✅ Shipped (2026-08-05), narrower than designed: an import-side gameserver validation gate (
galaxy_validation.py) re-checking structural/referential integrity of the translatedInsertPlan— not a re-check of bang's per-formation placement rules or content-layer counts. - ✅ Shipped: rejecting a structural-integrity violation with
ERR_BANG_VALIDATION_FAILED. - What actually happens today:
_validate_universe_shape(shape-only) runs first, thenvalidate_insert_plan_or_raise/validate_region_plan_or_raise(structural/referential) run on the translated plan; either can raise. Per-formation placement and content-layer invariants are still enforced only bang-side, not re-checked on import.
Today, bang's own correct-by-construction guarantee (step 2) is the only enforcement of these invariants — there is no independent gameserver-side cross-check to catch a divergence between bang's internal logic and its stated guarantees. The equivalence-drift question this contract was meant to close remains open until SK20 ships.
The contract is load-bearing on Universe.version — bang and gameserver release in lockstep on contract bumps. Adding a field to the bang payload, changing a field's shape, or relocating an invariant from one side of the boundary to the other is a contract change and bumps the version.
Inputs¶
The pipeline accepts one of two equivalent input shapes:
UniverseJSON as emitted bysw2102-bang/src/serialize.ts:universeToJSON. Thesectorsmap is rehydrated from the keyed object on read.bangschema rows — abang.universesrow plus its dependentbang.sectors,bang.warps,bang.clusters,bang.special_formations,bang.ports,bang.planets,bang.nebulae,bang.nav_hazards,bang.npc_rosters,bang.special_locationsrows. Used by Path B (bootstrap script) when the generator has run in service mode.
Per ADR-0069, commodity-pricing and quantity constants are passed into bang as part of its generation request — the gameserver owns balance tuning, but bang's deterministic seeding pass executes inside bang. Initial inventory values therefore arrive in the payload from bang already populated; the import pipeline does not re-seed them.
In addition to the bang payload, the caller supplies:
- Target region context. Which gameserver
Regionis being created from this Universe. One of: central_nexus— bootstrap of the universal hub. Forces 5,000 sectors, singleEXPANSEzone, 20-cluster overlay.terran_space— bootstrap of the starter region. Forces 300 sectors, three zones (Fed/Border/Frontier), governance autocracy at 2% tax.player_owned— region purchased by a player. Sector count and tier come from the subscription; the owner'sUser.idmust be supplied.- Optional admin overrides. A small dict of overrides applied after the per-region defaults: custom
faction_influenceweights, customgovernance_type, customtax_rate, customspecial_features. Overrides go through the same validation as the defaults. - Idempotency key. A stable identifier for this import attempt. Default:
Universe.seedcombined withUniverse.confighash. Used to detect re-runs of the same import.
The pipeline does not read live game state (no players, no transactions, no live presence); it only reads the bang payload, the Region row, and the optional overrides.
Process¶
The pipeline runs as a single database transaction with the numbered steps below. Each step's preconditions are validated before the next step begins. Per ADR-0069, the pipeline is narrower than a content-derivation pipeline: bang emits clusters, formations, station/planet inventories, citadel content, and NPC rosters as part of the payload, and the translator's job is to validate, scaffold, glue, and translate, not to derive.
1. Validate input¶
Before any write, the validator (services/gameserver/src/services/bang_schema.py — target) checks:
Universe.versionis in the supported version allowlist. Unknown versions abort with a structured error; no rows are written. The allowlist is a constant in the validator module and is bumped in lockstep with bang releases.Universe.totalSectors === Object.keys(Universe.sectors).length— the declared count matches the actual sector map size. Drift aborts the import.- Every
Warp.fromandWarp.toexists inUniverse.sectors. Orphaned warps are logged and dropped (non-fatal). Universe.fedspaceSectorsis a sorted contiguous range starting at sector 1. Length equalsconfig.fedspaceSize.Universe.specialLocationsreferences only sector IDs present inUniverse.sectors.Universe.clusterspartitions[1, totalSectors]exactly with no gaps and no overlaps. For Nexus imports, the cluster list matches the canonical 20-name overlay in./central-nexus-clusters.md.Universe.specialFormationsreferences only sector IDs present inUniverse.sectors. The 9-type catalog is enforced; noWARP_SINKinterior intersects anyBUBBLE/DEAD_END_BUBBLE/GOLD_BUBBLEinterior (cross-check of the invariant bang itself enforced at generation time).Universe.npcRostersreferences only sector IDs present inUniverse.sectors. Roster shapes (kind,factionCode,targetCount,hostSectorId,namePool) match the schema in../FEATURES/gameplay/police-forces.md.
Validation failures that abort the import never produce partial writes; the transaction is never opened.
2. Create or upsert the Galaxy row¶
The Galaxy is a singleton; the pipeline reads-or-creates by name. The row stores the bang Universe.seed and Universe.config in Galaxy.density.resource_distribution and a translator-specific bang_universe_id extension on Galaxy.statistics for idempotency tracking.
namefrom caller; default"Sectorwars 2102"for the singleton.max_sectorsset to the sum of all regions the operator plans to import (caller hint; default 10,000).expansion_enabled = true,resources_regenerate = true,warp_shifts_enabled = true,default_turns_per_day = 1000.statistics,density,state,events,combat_penalties,economic_modifiersJSONBs initialized fromservices/gameserver/src/core/galaxy_defaults.py(target).statistics.bang_universe_idset to the import's idempotency key. If a Galaxy row already exists with the same key and identicalconfig, the pipeline returns early as a no-op.
3. Create the Region row¶
Exactly one Region is created per pipeline invocation. The region_type, governance defaults, and starter ship come from the target region context:
| Context | region_type |
governance_type |
tax_rate |
total_sectors |
subscription_tier |
|---|---|---|---|---|---|
central_nexus |
central_nexus |
autocracy |
0.00 |
5000 | n/a (no owner) |
terran_space |
terran_space |
autocracy |
0.02 |
300 | n/a (no owner) |
player_owned |
player_owned |
from override or autocracy |
from override or 0.10 |
from subscription tier | from subscription tier |
The Region's language_pack, aesthetic_theme, traditions, social_hierarchy, trade_bonuses JSONBs default to empty objects and are populated post-import by the regional governance flow.
For initial bootstrap, the operator runs the pipeline twice: once with central_nexus against a 5,000-sector bang Universe, then again with terran_space against a 300-sector bang Universe. The two imports produce distinct Region rows under the same Galaxy.
4. Partition zones¶
Zone partitioning is driven entirely by the target region context, not by anything in the bang payload (bang has no zone concept):
central_nexus— oneZonerow:zone_type = EXPANSE,name = "The Expanse",start_sector = 1,end_sector = 5000,policing_level = 3,danger_rating = 6.terran_spaceandplayer_owned— threeZonerows split by sector number:FEDERATION— first 33% of sectors —policing_level = 9,danger_rating = 1.BORDER— middle 34% —policing_level = 5,danger_rating = 4.FRONTIER— last 33% —policing_level = 2,danger_rating = 8.
The split-percentage rounding favours FEDERATION on the low end and FRONTIER on the high end so that start_sector/end_sector ranges are contiguous and cover the full sector range with no gaps.
5. Import cluster rows¶
Per ADR-0069, clusters arrive in the bang payload as named, sector-range-bounded entities; the translator imports them as-is rather than deriving them from sector totals. For each Universe.clusters[i]:
name←bang.cluster.name(AI-generated per ADR-0044).type←bang.cluster.type(one ofSTANDARD,RESOURCE_RICH,POPULATION_CENTER,TRADE_HUB,MILITARY_ZONE,FRONTIER_OUTPOST,CONTESTED,SPECIAL_INTEREST).- Sector-range membership is stored on
Sector.cluster_id(set in step 7); the cluster's own range bounds are kept inCluster.special_featuresfor diagnostic queries. statsJSONB initialized with zeros; recomputed in step 16.warp_stability,economic_value,is_discovered,is_hidden,(x_coord, y_coord, z_coord)← bang-emitted values where present, otherwise default per cluster type.faction_influence← not set here; seeded gameserver-side in step 15 from the per-zone profile (faction enum is gameserver-side state).
For Nexus imports, bang emits the canonical 20-cluster overlay specified in ./central-nexus-clusters.md: 20 named clusters in declaration order, 250 sectors per cluster, fixed types (8 TRADE_HUB / 4 POPULATION_CENTER / 8 STANDARD), 5×4 grid layout. The translator validates the overlay matches the canonical list (count, names, sector-range partition) and rejects mismatches.
6. Import special formations¶
Per ADR-0069, SpecialFormation rows are stamped by bang during generation, between proximity-warp placement and long-distance-tunnel placement, and arrive in the payload ready to import. For each Universe.specialFormations[i]:
type← bangformation.type(one of the 12-type catalog:BUBBLE,DEAD_END_BUBBLE,GOLD_BUBBLE,TUNNEL,DEAD_END,WARP_SINK,BACKDOOR,BLISTER,ESCAPE_HATCH, plus the lost-formation setLOST_SECTOR/LOST_CLUSTER/ARCHIPELAGOadded in ADR-0070). The import logic is unchanged for the new types — still a row-copy from payload to gameserver table; the enum grew, the algorithm did not.anchor_sector_id← UUID of the gameserverSectorat bangformation.anchorSectorId.interior_sector_ids← ARRAY of UUIDs from the imported sector rows.propertiesJSONB ← bangformation.properties(1:1 copy; type-specific keys per../DATA_MODELS/special-formations.md).region_id← the Region created in step 3.is_discovered = false,discovery_requirement← default per formation type.generation_seed←Universe.seed(audit trail).
The translator does not stamp formations or rerun the stamping algorithm — bang emits the rows correct-by-construction, including the ADR-0046 invariant ("no WARP_SINK inside a BUBBLE"). A gameserver-side Phase 13 cross-check of this invariant on import is design-only, not built (see § Canonicality contract) — bang's correct-by-construction guarantee is the only enforcement today.
7. Assign sectors to cluster + zone¶
Each bang Sector becomes a gameserver Sector row. The translator iterates sectors in numeric order and assigns:
sector_id(integer) ← bangSector.id.sector_number(nullable integer) ←Sector.idfor Nexus sectors so admin queries can use it; null elsewhere.name← bangSector.beaconif non-null, else generated as"Sector {id}".cluster_id← the cluster created in step 5 that owns the sector's number range.zone_id← the zone created in step 4 that contains the sector's number, ornullif no zone covers it.region_id← the Region created in step 3.security_level,development_level,traffic_level← drawn uniformly from the cluster type's profile (Nexus, biased byTRADE_HUB/POPULATION_CENTER/STANDARD) or the zone profile (Terran/player-owned). Zone defaults: FEDERATION (8/7/7), BORDER (5/5/5), FRONTIER (2/3/3).is_discovered← bangSector.explored(the bang generator marks fedspace and special-location sectors as discovered).(x_coord, y_coord, z_coord)— version-dependent:Universe.versionparses as>= 1.1.0(including pre-release suffixes like1.1.0-pre.0): consumebang Sector.position.{x, y, z}directly. Bang emits these as integers scaled by 10000 to match the gameserver'sInteger not nullschema (DATA_MODELS/galaxy.md); translator copies as-is, no rescale or rounding.Universe.versionis1.0.0: bang did not emit positions. Interpolate from the Hilbert packing used in step 5 (legacy synthesis path). The translator must NOT mix the two paths within a single import.radiation_level = 0.0,hazard_level = 0unless overridden by step 8.resources← default JSONB fromDATA_MODELS/jsonb-schema.md#sectorresources. Asteroid yields stay zero unless the cluster type isRESOURCE_RICH, in which case the translator rolls non-zeroore/precious_metals/quantum_shardsfrom the cluster'sresource_distribution.defenses,players_present,ships_present,active_events,nav_beacons,nav_hazards← defaults fromDATA_MODELS/jsonb-schema.md.controlling_faction = null.
8. Mark sector special types¶
Tip status: ✅ tip-shipped via PR #739 (mergeCommit
5969c9e5;services/gameserver/tests/unit/test_bang_step8_special_types.pyPRESENT). (Re-verified 2026-08-21 vs origin/feat46bce720.) Design magnitudes below are existing canon targets — not newly invented here.
Sector types are assigned in two passes:
- Nebula pass. Every bang
Sector.nebula != nullbecomes a gameserverSectorwithtype = NEBULA. The bang nebula'stypeanddensityare translated through the table in the appendix and written into the owningCluster.nebula_propertiesJSONB (the gameserver carries nebula classification at the cluster level for atmospheric/colour metadata; the per-sectorSector.type = NEBULAflag is what gates sensor/combat effects). - Special-type pass. From the remaining sectors, 15% are flagged as special types. Per-cluster regional profile drives the distribution:
RESOURCE_RICHclusters → 60%ASTEROID_FIELD, 40%STANDARD.MILITARY_ZONEclusters → 80%STANDARD, 10%RADIATION_ZONE, 10%WARP_STORM.FRONTIER_OUTPOSTclusters → 70%STANDARD, 15%BLACK_HOLE, 15%RADIATION_ZONE.- All others → 85%
STANDARD, 15% split evenly acrossASTEROID_FIELD,RADIATION_ZONE,WARP_STORM,BLACK_HOLE.
The special-type roll is seeded from Universe.seed and the sector number so re-imports produce identical assignments.
9. Identify isolated clusters¶
Tip status: ✅ tip-shipped via PR #744 (mergeCommit
2237ca40;services/gameserver/tests/unit/test_bang_step9_isolated_clusters.pyPRESENT; Soft-HOLD #1349 DONE). (Re-verified 2026-08-21 vs origin/feat46bce720.) Design prose below is existing canon — not newly invented here.
After all sectors are written but before warps are translated, the pipeline runs a graph traversal over the bang warp list to compute connected components. A cluster is is_isolated = true when none of its sectors is reachable from a fedspace sector via natural warps.
Target: 10–20% of clusters are isolated. If the natural traversal yields fewer isolated clusters, the translator does not synthesize isolation; the bang seed determines reachability and the gameserver inherits it. If the natural traversal yields more than 25%, a warning is logged but the import proceeds — the operator should re-roll the bang seed if isolation is undesirable.
Sectors in isolated clusters require a Warp Jumper Quantum Jump to reach (see ../FEATURES/galaxy/sectors.md#quantum-jump-warp-jumper); the movement service reads Cluster.is_isolated to gate movement. (is_isolated is stored in Cluster.special_features as the string "isolated" since Cluster does not carry a dedicated boolean column.)
10. Translate warps¶
Tip status: ✅ tip-shipped via PR #745 (mergeCommit
67071398;services/gameserver/tests/unit/test_bang_step10_long_warp_tunnels.pyPRESENT; Soft-HOLD #1350 DONE). (Re-verified 2026-08-21 vs origin/feat46bce720.) Design prose below is existing canon — not newly invented here. Lightweightsector_warpsmapping remains; long-warpWarpTunnelcreation is tip-shipped.
Each bang Warp{from, to, oneWay} becomes one row in the sector_warps association table:
source_sector_id← UUID offrom.destination_sector_id← UUID ofto.is_bidirectional = !oneWay.turn_cost = 1(default; bang has no turn-cost concept).warp_stability = 1.0(default; degraded stability is a runtime concern).
When a bang warp connects sectors more than N numbers apart (heuristic threshold: max(50, totalSectors / 20)), the pipeline additionally creates a WarpTunnel row:
type = NATURAL,status = ACTIVE,is_bidirectional = !oneWay. (type = ARTIFICIALwithcreated_by_player_id = NULLis reserved for engineered low-sector bridges; bang's procedural warps map toNATURAL.)stability = 1.0,stability_enum = STABLE.propertiesJSONB initialized fromDATA_MODELS/jsonb-schema.md#warptunnelpropertieswithtraversal_cost = 1,discovered = true,affected_by_storms = false.source_endpoint/destination_endpointJSONBs populated with the source and destination sector/cluster/region IDs and coordinates.
The sector_warps row is the lightweight association used by routing; the WarpTunnel row carries the heavier metadata (lifecycle, decay, discovery). Both reference the same sector pair.
11. Translate ports → stations¶
Each bang Port becomes a gameserver Station with a co-created Market and per-commodity MarketPrice rows. Mapping:
name← bangPort.name.sector_uuid← UUID of the bang sector hosting the port.sector_id← integer bang sector ID (denormalized for read paths).station_class←StationClass.CLASS_<n>forn = 0..8. Bang class 0 (Stardock-only) maps to gameserverCLASS_0; bang classes 1–8 map directly. Gameserver classes 9–11 are reserved for higher-tier stations created post-import (Nova / Luxury / Advanced Tech Hub).type(enumStationType) ← derived from class viaservices/gameserver/src/core/station_class_map.py(target): bang class 1 →MINING, class 2 →INDUSTRIAL(agricultural), class 3 →INDUSTRIAL, class 4–7 →TRADING, class 8 →TRADINGwithis_quest_hub = true, class 0 →DIPLOMATIC.status = OPERATIONAL,size = 5,faction_affiliationfrom the cluster'scontrolling_faction.commoditiesJSONB ← see port commodity projection below.trader_personalityJSONB ← sampled from the default-distribution pool. Pool: 40%BORDER, 25%FEDERATION, 20%FRONTIER, 10%LUXURY, 5%BLACK_MARKET. Stations in FEDERATION zones bias towardFEDERATION; in FRONTIER zones bias towardFRONTIER.services,defenses,acquisition_requirementsJSONBs ← class-appropriate defaults fromservices/gameserver/src/core/station_defaults.py(target).
Port commodity projection (9-key wire). Bang emits each port's commodities block under the gameserver-canonical nine keys — ore, organics, equipment, fuel, luxury_goods, gourmet_food, exotic_technology, colonists, precious_metals — via sw2102-bang's TRADING_PATTERNS table (src/content.ts), enforced at generation time by validator rule B-200. The legacy port key fuel_ore was renamed to ore (2026-05-10). Each commodity's quantity is the initial seeded value (per ADR-0069, seeded by bang's deterministic pass using gameserver-supplied pricing constants). Status and cross-links: ../FEATURES/economy/trading.md, ../OPERATIONS/bang-integration.md.
The translator does not invent a 3→8 expansion. bang_import_service._build_full_commodities projects bang's payload onto COMMODITY_WIRE_ORDER (all nine keys). Bang may omit keys the station does not actively trade; omitted keys are filled from _COMMODITY_DEFAULTS (capacity / production_rate / price_variance / base_price) with quantity defaulting to 0. Bang action (B/S) maps to provisional buys/sells flags, then apply_class_pattern (core/station_class_map.py) fully overrides those flags from the station class pattern and stocks the station for first-dock tradeability (seeded per station so re-import is byte-identical).
A Market row is created per station with the standard volatility defaults; one MarketPrice row is inserted per commodity-station pair.
12. Translate planets¶
Each bang Planet becomes a gameserver Planet row in the same sector:
name← bangPlanet.name.sector_uuid/sector_id← bang sector hosting the planet.type← gameserverPlanetTypeper the appendix table.status = UNINHABITABLEunless the planet is at a special location (Terra →DEVELOPEDwith population hub).owner_id = null,population = 0,colonists = bang.colonists. Bang planet wire keys areore/organics/equipment(sw2102-bangPlanetinsrc/types.ts— notfuelOre/fuel_ore). The gameserver translator maps bangore→ planet columnfuel_oreviafuel_ore=int(p.get("ore", 0))inbang_import_service.py(planet/citadel domain still spells the columnfuel_ore; seecommodity_economy.pyvocabulary note).organics/equipmentpass through same-name. Per ADR-0069, these are the initial seeded values.resourcesJSONB initialized from the gameserver default; bang's planetary stockpiles seed the integer columns directly.productionJSONB ←{"fuel": 0, "organics": 0, "equipment": 0, "research": 0}.- Citadel: if bang
Planet.citadelis non-null, writecitadel_level = bang.citadel.level,citadel_drone_capacity = bang.citadel.droneCapacity, andcitadel_safe_contents = bang.citadel.safeContents(per ADR-0069 bang emits the citadel state including drone capacity and safe contents). Otherwise leave defaults at 0.
13. Special locations¶
Each bang SpecialLocation is materialized into the gameserver via location-specific rules:
- Terra — placed at the target region's Capital Sector (
Region.capital_sector_number). For Terran Space the Capital is fixed at sector 1; for the Nexus the Capital is anchored in the Gateway Plaza cluster; for player regions the Capital was randomized within the Federation Zone before the import reaches this step. The host Sector getsspecial_features = ["terra", "homeworld"]. APlanetrow of typeTERRAwithstatus = DEVELOPED, large population, max citadel level, and ownership defaulting to the Terran Federation faction is created. - Stardock — Class-0
Stationnamed "Stardock" withis_quest_hub = true,is_player_ownable = false, all services enabled. Hosting sector getsspecial_features = ["stardock"]. - Rylan / Alpha Centauri / Fringe homeworld — host Sector gets
special_featurescontaining the location slug; if the location is canonically a homeworld, an associatedPlanetof the appropriatePlanetTypeis created with faction ownership. - SpaceDock anchor — for Nexus imports, the pipeline ensures the Capital Sector (Gateway Plaza cluster) carries the SpaceDock special feature regardless of bang output. If bang did not place SpaceDock at the Capital, the translator upserts the SpaceDock station and demotes any conflicting bang-placed station to a normal port.
All special-location upserts are idempotent on (region_id, special_features contains <slug>).
13.5. Import NPC rosters¶
Per ADR-0069 and ../FEATURES/gameplay/police-forces.md, bang emits NPCRoster rows for both the Federation Marshal squads (Phase 12.5a — region rosters, non-Nexus only) and the Nexus Sentinel Corps (Phase 12.5b — Nexus rosters), plus pirate-tier rosters for Phase 12.6 holdings. The translator imports them as-is:
kind← bangroster.kind(e.g.,federation_marshal,nexus_sentinel,pirate_lord,pirate_captain,pirate_enforcer).faction_code← bangroster.factionCode.target_count← bangroster.targetCount.host_sector_id← Integer globalsectors.sector_idof the gameserverSectorat bangroster.hostSectorId(not a UUID — FINDINGS Phase-3 code-wins).name_pool← bangroster.namePool(JSONB).region_id← the Region created in step 3.
For Nexus imports, the translator additionally sets Sector.is_nexus_protected = true on the Capital sector and the Gateway Plaza cluster's sectors (per ../FEATURES/gameplay/police-forces.md Phase 12.5b).
The translator does not materialize NPCCharacter rows. Phase 12.5c (npc_scheduler.bootstrap_region(region_id)) runs as a post-commit hook (see step 17 below) outside the worldgen transaction so a roster-spawn failure doesn't block region creation.
14. Fedspace zone mapping¶
Bang's fedspaceSectors array (sector IDs 1 through config.fedspaceSize) drives Federation security in the gameserver:
- Each fedspace sector's
Sector.security_level = 10(overrides whatever was rolled in step 7). - Each fedspace sector is assigned to the FEDERATION zone for Terran/player-owned regions, or to the EXPANSE zone for Nexus.
- The sector's
controlling_factionis set to"terran_federation".
For the Nexus (which has only one zone), fedspace sectors retain EXPANSE zone but receive maximum security level — the EXPANSE zone's policing/danger ratings are unchanged.
15. Faction influence seeding¶
Tip honesty (2026-08-21 vs origin/feat
46bce720):services/gameserver/src/core/faction_profiles.pyis ABSENT on tip. Zone-profile seed +RegionInsertPlan.faction_influence_overridesare not tip-shipped. Land path: OPEN Soft-ORDER Fibril#1635/#1636· PR #638 / PR #640 (tip_HAS_land=NO). Do not read the table below as live tip behavior.
Galaxy.faction_influence and per-Cluster.faction_influence JSONBs are seeded from a per-zone-type default profile in services/gameserver/src/core/faction_profiles.py (target):
| Zone type | terran_federation | mercantile_guild | frontier_coalition | astral_mining_consortium | nova_scientific_institute | fringe_alliance |
|---|---|---|---|---|---|---|
| FEDERATION | 80 | 15 | 0 | 2 | 2 | 1 |
| BORDER | 30 | 30 | 20 | 10 | 5 | 5 |
| FRONTIER | 5 | 10 | 60 | 10 | 5 | 10 |
| EXPANSE | 25 | 25 | 15 | 15 | 10 | 10 |
Each row sums to 100. dominant_faction is computed as argmax. Admin overrides applied at step 3 are merged into the cluster-level influence post-seed (with renormalization to keep the sum at 100).
The Galaxy.faction_influence row is the area-weighted average of all clusters in the galaxy.
16. Recompute Galaxy aggregates¶
The final step calls Galaxy.update_statistics() (or its equivalent target service method) to recompute:
Galaxy.statistics.total_sectors,discovered_sectors,station_count,planet_count,warp_tunnel_count,genesis_count(last is 0).Galaxy.density.station_density,planet_density,one_way_warp_percentage,resource_distribution.Galaxy.state.exploration_percentage(≈ count ofis_discoveredsectors /total_sectors).- Per-
Cluster.statsJSONB values (total_sectors,populated_sectors,resource_value, etc.).
The transaction is committed at the end of step 16. If any earlier step raised, the entire transaction is rolled back and the gameserver schema returns to its pre-import state.
17. Post-commit NPC bootstrap (out-of-transaction hook)¶
After the worldgen transaction commits, the pipeline fires a one-time npc_scheduler.bootstrap_region(region_id) call (Phase 12.5c per ../FEATURES/gameplay/police-forces.md) that materializes the initial roster of named NPCCharacter rows from each NPCRoster.name_pool. This step happens outside the worldgen transaction so a roster-spawn failure does not block region creation; if it fails, NPC scheduler Loop B picks up the slack on its next 10-minute tick.
bootstrap_region is idempotent on (region_id, roster_id) — a second invocation finds the already-spawned characters and returns without re-spawning.
Outputs / state changes¶
A successful import inserts (or upserts):
- 1 Galaxy row (upserted by name; statistics recomputed).
- 1 Region row (always inserted; the same Universe imported into a different region context produces a different Region row).
- 1 to ~3 Zone rows depending on region context (1 for Nexus, 3 for Terran/player-owned). The Nexus's 20-cluster overlay lives in the
Clusterrows, not as additional zones. - C Cluster rows where C =
Universe.clusters.length(Nexus = 20, Terran ≈ 6, player-owned scaled per bang's emitted overlay). - F SpecialFormation rows where F =
Universe.specialFormations.length. - N Sector rows where N =
Universe.totalSectors. - M
sector_warpsrows where M =Universe.warps.length. - 0 to M' WarpTunnel rows where M' is the count of long-distance warps (typically 5–15% of warp count).
- K Station rows where K = number of bang sectors with a non-null
port. - K Market rows (one per Station).
- K × 9 MarketPrice rows (one per Station per commodity on the nine-key wire).
- J Planet rows where J = sum of
bang.Sector.planets.lengthacross all sectors. - R NPCRoster rows where R =
Universe.npcRosters.length. - S
Sector.special_featuresupdates for each bangSpecialLocation.
Out-of-transaction (step 17, post-commit):
- 0 to ΣR.target_count NPCCharacter rows materialized by
npc_scheduler.bootstrap_region.
Side effects:
Galaxy.statistics,Galaxy.density, and per-Cluster.statsJSONBs are recomputed.- No realtime-bus events are emitted during import. The import is a bulk operation; the bus is signalled once at completion (
galaxy.importedevent) so dashboards can refresh, but no per-row events fire. - No player/team/ship rows are touched.
- No reputation, market-transaction, or audit-log rows are written.
The transaction commits or aborts atomically.
Invariants¶
- Sector count match. Total sectors written equals
Universe.totalSectors. If the count drifts during step 7 (a sector failed to insert), the transaction aborts. - Cluster membership. Every Sector belongs to exactly one Cluster.
Sector.cluster_idis non-null and references a Cluster within the same Region. - Region membership. Every Sector belongs to exactly one Region.
Sector.region_idmatches the Region created in step 3 for every Sector inserted by this run. - Zone membership. Every Sector either belongs to one Zone whose sector range contains the sector's number, or
Sector.zone_idis null only when no zone covers the sector's range (which only happens if the operator passes a region-context that omits zones — not a default). - Nexus cluster sum. For Nexus imports, the 20 clusters cover exactly 5,000 sectors with no gaps and no overlap. Bang's emitted cluster list must match the canonical 20-name overlay; mismatches abort at step 1.
- Formation invariant. No
WARP_SINKformation interior intersects anyBUBBLE/DEAD_END_BUBBLE/GOLD_BUBBLEinterior (per ADR-0046). Bang enforces this internally. Corrected 2026-08-04: neither the translator's step-1 shape check (_validate_universe_shapechecks required-key presence, version-prefix, and sector-count only) nor a gameserver Phase 13 gate re-checks this invariant on import — bang's internal enforcement is the sole guarantee today. - Roster shape. Every
NPCRoster.host_sector_idresolves to a sector in the same region;target_count >= 1;name_poolis non-empty. - Warp referential integrity. Every
sector_warpsrow references two existing sectors; orphaned warps are dropped at validation time, never written. - Schema version.
Universe.versionis in the supported allowlist; no row is written for an unknown version. - Idempotency. Re-running the pipeline on the same
Universe.seed + configagainst the same Region context is a no-op. Detection is viaGalaxy.statistics.bang_universe_id(or an equivalent translator-private extension). - Faction influence sums. Per-Cluster
faction_influencenumeric values sum to 100;dominant_factionmatchesargmax. - Determinism. Two runs with the same
Universe.seed,config, and target region context produce identical row contents. Norandom.random()calls outside seeded RNG. - Single transaction. All writes occur in one transaction; partial failure leaves zero rows.
Failure modes¶
- Version mismatch.
Universe.versionnot in the allowlist → abort at step 1; structured error includes the supplied version and the allowlist. No partial writes. - Sector count drift.
Universe.totalSectors !== Object.keys(sectors).length→ abort at step 1. - Orphaned warps. Warp references a missing sector → log + skip the warp; import continues. The skipped warp is reported in the import summary.
- Faction profile missing. Zone type has no entry in
faction_profiles.py→ fall back to the neutral profile (each faction at 16-17, sum to 100,dominant_faction = "contested"). - Cluster generation oversampling. Standard distribution rolls produce too few
STANDARDclusters to satisfy invariants → fall back toSTANDARDfor any cluster that would have been a special type. - Idempotency mismatch. A
Galaxy.statistics.bang_universe_idalready exists with a differentconfighash → abort with explicit"use --force-reimport to override"hint. The--force-reimportflag deletes the prior Galaxy / Region / Cluster / Sector cascade and re-runs. - Cluster sum mismatch. Bang's emitted cluster list does not partition
[1, totalSectors]exactly (gap, overlap, or — for Nexus — name/count drift from the canonical 20-cluster overlay) → abort at step 1. - Formation invariant violation. Bang emitted a formation set that violates ADR-0046 ("no
WARP_SINKinside aBUBBLE") → abort at step 1 with the offending(sink_id, bubble_id)pair listed. - Roster validation failure. A roster references a missing host sector or carries an empty
name_pool→ abort at step 1. - Special location collision. Two different special locations claim the same sector → abort with the conflicting location list.
- Database transaction failure. Any underlying constraint violation → entire transaction rolls back; the operator sees the underlying SQL error in the structured response.
- Long-running import. Imports of full 5,000-sector Nexus universes can take 60–120 seconds; the operator must run the pipeline outside the realtime request path (see the operations doc for CLI / async-job invocation modes).
Source map¶
| Concern | Path |
|---|---|
| Translator entry point | services/gameserver/scripts/bootstrap_from_bang.py (target) |
| Admin trigger | services/gameserver/src/api/routes/admin/galaxy.py:POST /admin/galaxy/import-bang (target) |
| Region/Zone/Cluster scaffolding | services/gameserver/src/services/galaxy_bootstrap_service.py (target) |
| Cluster overlay validator (Nexus) | services/gameserver/src/services/nexus_generation_service.py:_validate_nexus_cluster_overlay (target — bang emits the overlay; gameserver validates it matches the canonical 20-name list) |
| NPC scheduler bootstrap hook | services/gameserver/src/services/npc_scheduler.py:bootstrap_region (target) |
| Commodity catalog defaults | services/gameserver/src/core/commodity_catalog.py (target) |
| Station class → type map | services/gameserver/src/core/station_class_map.py (target) |
| Station defaults (services, defenses, acquisition) | services/gameserver/src/core/station_defaults.py (target) |
| Faction influence profiles | services/gameserver/src/core/faction_profiles.py (target) |
| Galaxy / Region / Cluster JSONB defaults | services/gameserver/src/core/galaxy_defaults.py (target) |
| Bang JSON schema validator | services/gameserver/src/services/bang_schema.py (target) |
| Bang DB reader (Path B) | services/gameserver/src/services/bang_db_reader.py (target) |
| Idempotency tracker | services/gameserver/src/services/galaxy_bootstrap_service.py:_already_imported() (target) |
Generator-side reference paths (not written by this pipeline; cited for context):
sw2102-bang/src/types.ts—Universe,Sector,Warp,Port,Planet,Nebula,NavHazard,SpecialLocationinterfaces.sw2102-bang/src/config.ts—BigBangConfig,DEFAULT_CONFIG,resolveConfig()clamps.sw2102-bang/src/bigbang.ts— generator orchestrator.sw2102-bang/src/serialize.ts—universeToJSON/universeFromJSON.sw2102-bang/db/init.sql— bang-side DB schema (used by Path B).
Related¶
../OPERATIONS/bang-integration.md— operational view: where the script runs, when, who triggers it, environment variables for the three invocation paths../central-nexus-clusters.md— 20-cluster organization invoked at step 6../galaxy-generation.md— broader generation pipeline that calls this translator.../DATA_MODELS/galaxy.md— target Galaxy / Region / Cluster / Zone / Sector / WarpTunnel shapes.../DATA_MODELS/jsonb-schema.md— JSONB key shapes forCluster.faction_influence,Sector.defenses,Station.commodities,WarpTunnel.properties, etc.../OPERATIONS/multi-regional.md— Region governance defaults consumed at step 3.
Appendix A — Bang port commodities → gameserver nine-commodity wire¶
Bang port commodities and gameserver Station.commodities share the same nine canonical keys (COMMODITY_WIRE_ORDER in services/gameserver/src/services/bang_import_service.py). There is no 3→8 expansion step — that prose was historical fiction retired by the 9-commodity expansion and the fuel_ore→ore rename (see trading.md Bang generator section).
| Key | When present in bang payload | When omitted |
|---|---|---|
ore |
quantity / capacity / regenRate (+ action → provisional buys/sells) |
_COMMODITY_DEFAULTS; quantity = 0 |
organics |
same | same |
equipment |
same | same |
fuel |
same | same |
luxury_goods |
same | same |
gourmet_food |
same | same |
exotic_technology |
same | same |
colonists |
same | same |
precious_metals |
same | same |
Projection is _build_full_commodities. Class-pattern finalization via apply_class_pattern then fully overrides bang's per-commodity B/S flags from the station class pattern and stocks the station so it is tradeable on first dock (see §11 and bang_import_service port→station path). Stations may carry stock for commodities they neither buy nor sell when defaults/pattern leave quantity present — the class pattern decides what the trade UI offers.
Appendix B — Bang NebulaeType → gameserver nebula classification¶
Bang emits two nebula types (normal and magnetic) with a per-nebula density value (1–100). The gameserver carries six named nebula classifications. The mapping is monotonic: high bang density / magnetic type maps to high quantum-field gameserver classifications (Crimson end); low density / normal type maps to low quantum-field classifications (Obsidian end). magnetic is uniformly higher-field than normal.
Bang NebulaeType |
Density range | Gameserver classification | Field strength | Secondary effect |
|---|---|---|---|---|
normal |
1 – 33 | obsidian |
0–20 | Sensor masking; severe warp disruption |
normal |
34 – 66 | amber |
20–40 | Hull damage hazard while traversing |
normal |
67 – 100 | violet |
40–60 | Rare-spawn bias (exotic encounters) |
magnetic |
1 – 33 | emerald |
50–70 | Production bonus on adjacent planets |
magnetic |
34 – 66 | azure |
60–80 | Stable warp formation in-cluster |
magnetic |
67 – 100 | crimson |
80–100 | Mild combat advantage; highest shard yield |
Selection is deterministic on (type, density) — zone bias on the gameserver side comes from the nebula-density sampling and zone-multiplier weighting in services/gameserver/src/services/bang_import_service.py:_finalize_cluster_nebula_fields (see ../FEATURES/galaxy/generation.md) and is independent of bang's mapping. A bang magnetic nebula with density 90 becomes crimson regardless of which zone its host sector lands in.
The chosen classification is written into the owning Cluster.nebula_properties JSONB:
{
"primary_nebula": "<classification>",
"density": <bang_density>,
"sectors": [<sector_numbers>],
"effects": {
"sensor_penalty": <derived>,
"combat_modifier": <derived>,
"warp_stability_penalty": <derived>
}
}
The per-Sector flag is the binary Sector.type = NEBULA (carried in the existing sector_type enum). The classification colour does not appear on the Sector row; it is read from the owning Cluster's JSONB by the UI and combat resolver.
Appendix C — Bang PlanetType → gameserver PlanetType¶
Bang PlanetType |
Gameserver PlanetType |
|---|---|
barren |
BARREN |
earth |
TERRA |
mountainous |
MOUNTAINOUS |
oceanic |
OCEANIC |
glacial |
ICE |
volcanic |
VOLCANIC |
Gameserver-only PlanetType values (DESERT, GAS_GIANT, JUNGLE, ARCTIC, TROPICAL, ARTIFICIAL) are not produced by the bang generator. They reach the universe through gameserver-side sources rather than the bang import: DESERT is a Genesis-rollable terraforming target; JUNGLE, ARCTIC, and TROPICAL are colonizable worlds the gameserver generates (and arrive via special encounters or operator-seeded sectors); GAS_GIANT is a real body that cannot be landed on or claimed. ARTIFICIAL is a reserved enum value with no generation path.
Translation defaults set status = UNINHABITABLE and habitability_score = 0 for all imported planets. The Terra special location is the one exception: its planet is upgraded to status = DEVELOPED, habitability_score = 100, population = 10_000_000_000, and full citadel during step 13.
Appendix D — Bang SpecialLocationType → gameserver Sector flags¶
Bang SpecialLocationType |
Sector special_features |
Additional state |
|---|---|---|
terra |
["terra", "homeworld"] |
Planet of type TERRA, status = DEVELOPED; faction owner terran_federation; placed at the region's Capital Sector |
stardock |
["stardock"] |
Station of class CLASS_0 named "Stardock"; is_quest_hub = true, is_player_ownable = false; all services enabled |
rylan |
["rylan", "homeworld", "alien"] |
Planet of type OCEANIC; faction owner nova_scientific_institute |
alpha_centauri |
["alpha_centauri", "stellar_landmark"] |
Planet of type TERRA with reduced habitability; no fixed faction |
fringe_homeworld |
["fringe_homeworld", "homeworld", "hostile"] |
Planet of type BARREN; faction owner fringe_alliance; sector controlling_faction = "fringe_alliance" |
cabal_hq (schema ≥1.3.6 / LEG-12) |
["special_location:cabal_hq"] |
Generic specialLocations map only (bang_import_service.py appends special_location:{type}). Themed Cabal port/planet arrive via normal bang port/planet translation — no dedicated faction-owner or planet-type override on this path. ✅ Shipped — _derive_sector_name returns "Cabal Headquarters" for cabal_hq (PR #644 MERGED → tip 46bce720). (Re-verified 2026-08-21 vs origin/feat 46bce720.) |
Special features are appended to the Sector's existing special_features ARRAY rather than replaced, so a sector that is both a Stardock host and inside the Fringe Alliance homeworld system would carry both slugs. (In practice, bang places these locations at distinct sectors.)
The SpaceDock anchor — described in step 13 — is not a bang SpecialLocationType. It is a gameserver-only convention for the Nexus Region, idempotently upserted into the Capital Sector (Gateway Plaza cluster).
Appendix E — Bang Port class → gameserver StationClass¶
| Bang Port class | Gameserver StationClass |
Gameserver StationType |
Trading pattern |
|---|---|---|---|
| 0 | CLASS_0 |
DIPLOMATIC |
special: buys/sells special_goods, sells colonists |
| 1 | CLASS_1 |
MINING |
buys ore; sells organics, equipment |
| 2 | CLASS_2 |
INDUSTRIAL |
buys organics; sells ore, equipment |
| 3 | CLASS_3 |
INDUSTRIAL |
buys equipment; sells ore, organics |
| 4 | CLASS_4 |
TRADING |
buys exotic_technology; sells ore, organics, equipment, fuel |
| 5 | CLASS_5 |
TRADING |
buys all four basics; sells luxury_goods |
| 6 | CLASS_6 |
TRADING |
buys ore, organics; sells equipment, fuel |
| 7 | CLASS_7 |
TRADING |
buys equipment, fuel; sells ore, organics |
| 8 | CLASS_8 |
TRADING (is_quest_hub = true) |
premium buyer: buys all four basics |
Gameserver classes 9–11 (CLASS_9 Nova premium seller, CLASS_10 Luxury Market, CLASS_11 Advanced Tech Hub) are reserved for higher-tier stations created post-import by the economy or quest systems; the bang generator does not produce them.
The bang Port.class === 0 case appears only at the Stardock special location; the translator treats Stardock as the single canonical CLASS_0 station per Region and rejects any additional CLASS_0 ports the bang payload contains.
Appendix F — Bang Cluster → gameserver Cluster¶
| Bang field | Gameserver column | Translation |
|---|---|---|
cluster.name |
Cluster.name |
Direct copy. AI-generated per ADR-0044. |
cluster.type |
Cluster.type |
Direct copy. One of STANDARD, RESOURCE_RICH, POPULATION_CENTER, TRADE_HUB, MILITARY_ZONE, FRONTIER_OUTPOST, CONTESTED, SPECIAL_INTEREST. |
cluster.sectorRangeStart / sectorRangeEnd |
Cluster.special_features ("sector_range_start", "sector_range_end" keys) |
Stored on the cluster for diagnostic queries; sector membership is materialized via per-sector Sector.cluster_id in step 7. |
cluster.warpStability, economicValue, isDiscovered, isHidden |
Cluster.warp_stability, etc. |
Direct copy where bang emits; otherwise default per cluster type. |
cluster.coords (x, y, z) |
Cluster.x_coord, y_coord, z_coord |
Direct copy. Bang lays Nexus clusters out on a 5×4 grid; standard regions use a Hilbert-packing on cluster sector-range. |
| (gameserver-only) | Cluster.faction_influence |
Not from bang. Seeded gameserver-side at step 15 from the per-zone profile in faction_profiles.py. |
Bang clusters arrive named, sector-range-bounded, and laid out spatially. The gameserver overlays faction-influence percentages because those depend on the gameserver's faction enum and per-customer admin overrides.
Appendix G — Bang SpecialFormation → gameserver SpecialFormation¶
The translation is a 1:1 import. Bang owns formation stamping (anchor selection, template application, validation) and emits ready-to-insert rows; the gameserver validates and persists.
| Bang field | Gameserver column | Translation |
|---|---|---|
formation.type |
SpecialFormation.type |
Direct copy. One of the 12-type catalog: BUBBLE, DEAD_END_BUBBLE, GOLD_BUBBLE, TUNNEL, DEAD_END, WARP_SINK, BACKDOOR, BLISTER, ESCAPE_HATCH, LOST_SECTOR, LOST_CLUSTER, ARCHIPELAGO (the last three added in ADR-0070). |
formation.anchorSectorId |
SpecialFormation.anchor_sector_id |
UUID of the imported sector at this bang ID. |
formation.interiorSectorIds |
SpecialFormation.interior_sector_ids |
ARRAY of UUIDs from imported sectors. |
formation.properties |
SpecialFormation.properties (JSONB) |
1:1 copy. Type-specific keys per ../DATA_MODELS/special-formations.md. |
| (gameserver-only) | SpecialFormation.region_id |
Set to the Region created in step 3. |
| (gameserver-only) | SpecialFormation.is_discovered |
Default false; per-player discovery handled at runtime per ADR-0045. |
| (gameserver-only) | SpecialFormation.generation_seed |
Set to Universe.seed for audit trail. |
A gameserver Phase 13 cross-check of the ADR-0046 invariant ("no WARP_SINK inside a BUBBLE") on the imported set is design-only, not built (see § Canonicality contract); bang enforces the same invariant at generation time, and that remains the only enforcement today.
Appendix H — Bang NPCRoster → gameserver NPCRoster¶
| Bang field | Gameserver column | Translation |
|---|---|---|
roster.kind |
NPCRoster.kind |
Direct copy. One of federation_marshal, nexus_sentinel, pirate_lord, pirate_captain, pirate_enforcer, etc. (see ../FEATURES/gameplay/police-forces.md and ADR-0047). |
roster.factionCode |
NPCRoster.faction_code |
Direct copy. |
roster.targetCount |
NPCRoster.target_count |
Direct copy. |
roster.hostSectorId |
NPCRoster.host_sector_id |
Integer global sectors.sector_id of the imported sector (not a UUID — FINDINGS Phase-3 code-wins). |
roster.namePool |
NPCRoster.name_pool |
JSONB array of candidate names; consumed by npc_scheduler.bootstrap_region at step 17. |
roster.defaultLodgingId |
NPCRoster.default_lodging_id |
Optional FK to an OutlawBase (pirate-tier rosters) or NPCBarracks (Marshal/Sentinel rosters). |
| (gameserver-only) | NPCRoster.region_id |
Set to the Region created in step 3. |
NPCCharacter rows are not imported — they are runtime-managed and materialized post-commit by npc_scheduler.bootstrap_region(region_id) (step 17). The data-model rule: NPCRoster is worldgen-stamped (now bang-stamped); NPCCharacter is runtime-managed.