Skip to content

Bang integration

Status: 🚧 Partial — Path A (admin job) and the core translator are fully wired; Path B bootstrap script is absent, version versioning logic is simplified. Re-corrected 2026-08-06 (supersedes 2026-08-04, now stale): a gameserver-side validation gate (galaxy_validation.py) shipped 2026-08-05 and runs structural/referential-integrity checks on import — but per-formation placement invariants (WARP_SINK/BUBBLE) and content-layer counts remain bang-side-only, not re-checked by the gate. See ../SYSTEMS/bang-import-pipeline.md § Canonicality contract for the precise coverage boundary.

Status. The sw2102-bang sidecar is the live generator path: the admin-ui's "Bang a New Galaxy!" form drives a bang generation job (Path A), and the gameserver's BangImportService translates its output and persists it. The in-process POST /admin/galaxy/generate endpoint returns HTTP 410. The contract documented below is the canonical bang↔gameserver boundary; the fully standalone-service split, the complete Universe JSON envelope, and the bang.* PostgreSQL schema remain launch-target. See ../SYSTEMS/galaxy-generation.md.

sw2102-bang is the standalone universe generator that produces a fresh galaxy. The gameserver consumes its output to bootstrap (or rebuild) the world. This page documents the contract between the two repos.

The generator lives at https://github.com/.../sw2102-bang. The gameserver is services/gameserver/ inside the Sectorwars2102 repo. The two communicate by writing to and reading from the shared PostgreSQL database; there is no HTTP coupling.

Two execution modes

sw2102-bang supports two entry points. Both produce the same Universe shape internally; they differ in where the result goes.

CLI mode — JSON to disk or stdout

bigbang [options]

The CLI writes the generated universe as JSON to stdout, an output file, or as a human summary. Use this for review, reproducibility checks, and offline tooling.

Source: sw2102-bang/src/cli.ts

Flag Default Meaning
--sectors, -s <n> 1000 Total sectors (clamped 20-20000)
--seed <n> random RNG seed; same seed + same config = same universe
--density <n> 20 Max course length, controls overall density (3-50). Recorded on Universe.config; enforcement semantics are pinned in a follow-up doc PR alongside bang's first enforcement implementation.
--two-way-warps <n> 30 Two-way warp percentage (5-100). Drives the FORCED bidirectional bucket size; the total observed bidirectional fraction also includes natural k-NN symmetry from the bulge density gradient and is reported in --summary.
--one-way-warps <n> 5 One-way warp percentage (0-25), the canon ~5% target ratified by ADR-0093 item 16 (WO-BANG-ONEWAY-ALGO; folded from ADR-0093, re-verified 2026-08-07). Drives the FORCED one-way bucket size; the remainder of pairs preserves natural k-NN symmetry.
--max-warps <n> 6 Max warps per sector (2-10). Strictly enforced via post-categorization drop; bidirectional warps count toward both endpoints' degree, one-way warps only toward the source.
--port-percent <n> omitted → densityFor(regionType) Sectors with ports (0-80). When omitted, schema 1.3.8 fills from region type: Central Nexus 5%, Terran Space / Player-Owned 15% (../FEATURES/galaxy/generation.md Step 2). Explicit value wins.
--planet-percent <n> omitted → densityFor(regionType) Sectors with planets (0-60). When omitted: Central Nexus 10%, Terran Space / Player-Owned 25%. Explicit value wins.
--nebula-percent <n> 5 Sectors in nebulae (0-30)
--fedspace <n> 10 Federation-protected sectors (2-20)
--stardock <n> 0 (auto) Stardock sector ID; 0 means auto-pick
--region-type <r> player_owned Region kind: player_owned | terran_space | central_nexus. central_nexus requires --sectors=5000 (canonical 20-cluster overlay). Invalid values exit 2.
--config-json <path\|-> GenerationRequest JSON from file or stdin (-). Individual CLI flags override conflicting fields (conflicts warn on stderr).
--output, -o <file> stdout Write JSON to file
--summary off Print a human-readable summary to stderr
--render off Print an ASCII map of the galaxy to stderr (3D → 2D projection with chalk colors). Density-binned grid; fedspace highlighted; cell-color gradient by sector count.
--render-axis <axis> top Projection axis for --render: top (XY plane), side (XZ plane), or iso (isometric).
--no-color off Disable chalk color codes in --render output. Useful for non-tty redirects, CI logs, and snapshot tests.
--compact off Compact JSON (no formatting)
--quiet, -q off Suppress progress output on stderr

The same options resolve through resolveConfig() (sw2102-bang/src/config.ts), which clamps inputs, auto-picks a seed when seed === 0, and (schema 1.3.8 / LEG-48) fills omitted portPercent/planetPercent via densityFor(regionType).

Service mode — write directly to PostgreSQL

The generator can also run as a one-shot container that writes to a database and exits. This is the integration path with the gameserver.

docker compose up generator

Source: sw2102-bang/src/db-writer.ts, sw2102-bang/Dockerfile.generator, sw2102-bang/docker-compose.yml

Configuration is via environment variables:

Env var Default Meaning
DATABASE_URL postgresql://tw2102:tw2102@localhost:5432/tw2102 Connection string
UNIVERSE_NAME default Identifier; reuse to overwrite
TW_SECTORS 1000 Total sectors
TW_SEED random RNG seed
TW_REGION_TYPE unset → player_owned via bigbang player_owned | terran_space | central_nexus — pass via -e / env (not wired as a compose environment: default; same pattern as optional TW_PORT_PERCENT / TW_PLANET_PERCENT). central_nexus requires TW_SECTORS=5000.
TW_PORT_PERCENT omitted → densityFor(regionType) Port density (0-80). Same region-derived defaults as --port-percent when unset; set env to override.
TW_PLANET_PERCENT omitted → densityFor(regionType) Planet density (0-60). Same region-derived defaults as --planet-percent when unset; set env to override.

The container waits for the database to accept connections, generates a universe, deletes any existing universe with the same name, and writes the new one in a single transaction.

Output shape — JSON

The CLI emits the Universe interface from sw2102-bang/src/types.ts, with the internal sectors map serialized as a keyed object. Per ADR-0069, bang emits a fully-formed region snapshot: the topology graph plus the named clusters, special formations, station and planet inventories, citadel content, and NPC rosters that go with it. Faction-influence values, the Region row, cross-region warp wiring, and runtime state remain gameserver-side.

Honesty (LEG-1147): bang wire commodity keys are gameserver-canonical — port JSON uses ore, not the retired fuel_ore name (sw2102-bang/src/content.ts TRADING_PATTERNS). The gameserver translator may still accept legacy aliases on ingest; do not treat fuel_ore as bang's primary emit key.

{
  "version": "1.1.0",
  "seed": 42,
  "totalSectors": 1000,
  "sectors": {
    "1": {
      "id": 1,
      "position": {"x": 0, "y": 0, "z": 0},
      "warps": [2, 3, 7],
      "port": {
        "name": "Capital Station",
        "class": 1,
        "black_market": false,
        "commodities": {
          "ore":       {"action": "B", "quantity": 1000, "capacity": 5000, "regenRate": 100},
          "organics":  {"action": "B", "quantity": 800,  "capacity": 3000, "regenRate": 80},
          "equipment": {"action": "S", "quantity": 500,  "capacity": 2000, "regenRate": 50}
        }
      },
      "planets": [
        {
          "name": "Terra",
          "type": "earth",
          "owner": null,
          "ore": 0,
          "organics": 0,
          "equipment": 0,
          "colonists": 0,
          "citadel": {
            "level": 1,
            "droneCapacity": 100,
            "safeContents": 0
          }
        }
      ],
      "navHazards": [
        {"type": "mine", "owner": null, "quantity": 4}
      ],
      "nebula": null,
      "beacon": null,
      "explored": true
    }
  },
  "warps": [
    {"from": 1, "to": 2, "oneWay": false}
  ],
  "clusters": [
    {
      "id": 1,
      "name": "Gateway Plaza",
      "sectorRangeStart": 1,
      "sectorRangeEnd": 250,
      "type": "TRADE_HUB"
    }
  ],
  "specialFormations": [
    {
      "id": "f-001",
      "type": "BUBBLE",
      "anchorSectorId": 412,
      "interiorSectorIds": [413, 414, 415, 416],
      "properties": {
        "interiorSize": 4,
        "linkTunnelDepth": 0
      }
    },
    {
      "id": "f-002",
      "type": "LOST_SECTOR",
      "anchorSectorId": 731,
      "interiorSectorIds": [731],
      "properties": {
        "exitWarp": null
      }
    },
    {
      "id": "f-003",
      "type": "LOST_CLUSTER",
      "anchorSectorId": 812,
      "interiorSectorIds": [812, 813, 814, "...", 833],
      "properties": {
        "interiorClusterId": 7,
        "exitWarp": { "sourceSectorId": 820, "destinationSectorId": 504 }
      }
    },
    {
      "id": "f-004",
      "type": "ARCHIPELAGO",
      "anchorSectorId": 850,
      "interiorSectorIds": [850, 851, "...", 949],
      "properties": {
        "memberClusterIds": [8, 9, 10],
        "crossWarpCount": 5,
        "exitWarps": [
          { "sourceSectorId": 905, "destinationSectorId": 612 }
        ]
      }
    }
  ],
  "npcRosters": [
    {
      "kind": "federation_marshal",
      "factionCode": "terran_federation",
      "targetCount": 9,
      "hostSectorId": 5,
      "namePool": ["..."]
    }
  ],
  "specialLocations": [
    {"type": "terra",      "sectorId": 1},
    {"type": "stardock",   "sectorId": 173},
    {"type": "rylan",      "sectorId": 401},
    {"type": "alpha_centauri", "sectorId": 9},
    {"type": "fringe_homeworld", "sectorId": 600}
  ],
  "fedspaceSectors": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  "config": { "...": "the BigBangConfig used to generate this universe" },
  "createdAt": "2026-04-30T18:00:00.000Z"
}

Field reference (from sw2102-bang/src/types.ts):

  • Sectors. Each sector has a position (integer 3D coordinates scaled by 10000, range roughly [-10000, 10000]), warps (an array of destination sector IDs), an optional port, an array of planets, an array of navHazards, an optional nebula, an optional beacon, and an explored flag.
  • Ports. Class 0 is reserved for special ports (Stardock); classes 1–8 encode buy/sell combinations across the nine gameserver-canonical commodities (ore, organics, equipment, fuel, precious_metals, gourmet_food, exotic_technology, luxury_goods, colonists) via bang's TRADING_PATTERNS table (sw2102-bang/src/content.ts). Per-class coverage is enforced at generation time by validator rule B-200 (requiredCommoditiesFor()). Each commodity tracks action (B for buying, S for selling), quantity, capacity, and regenRate. Per ADR-0069, quantity is the initial seeded value produced by bang's deterministic seeding pass (the gameserver translator passes pricing and seeding constants in as inputs so balance tuning stays gameserver-controlled, but the seed run itself executes inside bang). Feature status lives at ../FEATURES/economy/trading.md.
  • Planets. Six types (barren, earth, mountainous, oceanic, glacial, volcanic). Generator output carries the planet's initial inventory in the flat ore, organics, equipment, and colonists fields (per ADR-0069, bang owns the seeded values — WO-BANG-PLANET-SEED-EMIT was greenlit alongside WO-BANG-ONEWAY-ALGO by ADR-0093 item 16, folded from ADR-0093, re-verified 2026-08-07) and, when a citadel is stamped, the citadel's level, droneCapacity, and safeContents (numeric credit total — bang stamps 0; populated safes are an operator-or-quest concern on the gameserver).
  • Citadels. Embedded inside each planet under citadel. level matches the existing field; droneCapacity follows the per-level table in ../FEATURES/planets/citadels.md; safeContents is a number (credit total) stamped 0 at generation time — bang does not emit the runtime vault object shape; the gameserver import maps it to citadel_safe_contents (see bang_import_service.py). Named-safe quests populate vault contents post-import.
  • Nav hazards. mine (Armid), limpet, or fighter (deployed); each carries an owner ID and quantity. Mine quantities per sector are bang's responsibility (already in scope; reaffirmed by ADR-0069).
  • Nebulae. normal (hides contents) or magnetic (disrupts navigation); 1-100 density.
  • Warps. A flat list paralleling each sector's warps array; each entry specifies from, to, and oneWay.
  • Clusters. One row per cluster in the region — name (AI-generated per ADR-0044), sectorRangeStart / sectorRangeEnd, and type (one of the standard cluster-type enum values). No faction-influence values — those depend on the gameserver's faction enum and stay gameserver-side, seeded post-import from the per-zone profile in ../SYSTEMS/bang-import-pipeline.md.
  • Special formations. One row per stamped formation — type (one of the 12 catalog values: BUBBLE, DEAD_END_BUBBLE, GOLD_BUBBLE, TUNNEL, DEAD_END, WARP_SINK, BACKDOOR, BLISTER, ESCAPE_HATCH, plus the lost-formation set LOST_SECTOR / LOST_CLUSTER / ARCHIPELAGO added in ADR-0070), anchorSectorId, interiorSectorIds, and a type-specific properties JSONB. Bang stamps lost formations first (Step 6.5a), then the rest of the catalog (Step 6.5b) between proximity-warp placement and long-distance-tunnel placement; long-distance tunnels (Step 7) skip lost-formation sector sets. Bang enforces invariants — notably ADR-0046's "no WARP_SINK inside a BUBBLE" and ADR-0070's "no external warps from a lost formation except the optional one-way exits" — internally so output is correct-by-construction. Re-corrected 2026-08-06 (supersedes 2026-08-04, now stale on the gate's existence though not its formation-placement claim): the gameserver still does not cross-check this specific WARP_SINK/BUBBLE placement invariant on import — galaxy_validation.py (shipped 2026-08-05, wired into bang_import_service.py) checks structural/referential integrity of formations (anchor/interior sectors exist in the plan) but not their placement semantics. _validate_universe_shape remains shape-only (required-key presence, version-prefix, sector-count) and runs first. Bang's own correct-by-construction guarantee is therefore still the only enforcement that exists for this specific invariant, even though a broader structural gate now runs on import.
  • NPC rosters. One row per worldgen-stamped roster: kind (e.g., federation_marshal, nexus_sentinel, pirate_captain), factionCode, targetCount, hostSectorId, and a namePool for runtime spawning. Bang emits the roster shapes specified by ../FEATURES/gameplay/police-forces.md Phase 12.5a (region rosters) and Phase 12.5b (Nexus rosters). No NPCCharacter rows — runtime materialization (Phase 12.5c) stays gameserver-side via npc_scheduler.bootstrap_region(region_id) after the import transaction commits.
  • Special locations. Terra, Stardock, Rylan, Alpha Centauri, Fringe Alliance homeworld; the generator places each one once.
  • Fedspace. Sectors 1 through fedspaceSize; protected from PvP.

Output shape — PostgreSQL

When run in service mode, the generator writes to the bang schema defined in sw2102-bang/db/init.sql:

Table Purpose Key columns
universes One row per generated universe. id PK, name UNIQUE, version, seed, total_sectors, config (JSONB)
sectors One row per sector. id PK, universe_id FK, sector_number, beacon, explored
warps Connections between sectors. id PK, universe_id FK, from_sector, to_sector, one_way
clusters One row per cluster (named, sector-range bounded). id PK, universe_id FK, name, type, sector_range_start, sector_range_end
special_formations One row per stamped formation. id PK, universe_id FK, type, anchor_sector_id FK, interior_sector_ids (ARRAY), properties (JSONB)
ports One row per port. id PK, sector_id FK, name, class, commodities (JSONB — wire keys include ore/organics/equipment/…; quantity is the initial seeded value)
planets One row per planet. id PK, sector_id FK, name, type, owner, commodity columns (ore/organics/equipment/colonists — initial seeded values), citadel_level, citadel_drone_capacity, citadel_safe_contents (JSONB)
nebulae One row per nebula sector. id PK, sector_id FK, type, density
nav_hazards One row per hazard placement. id PK, sector_id FK, type, quantity, owner
npc_rosters One row per worldgen-stamped roster. id PK, universe_id FK, kind, faction_code, target_count, host_sector_id FK, name_pool (JSONB)
special_locations Terra/Stardock/etc. id PK, universe_id FK, type, sector_number

The writer deletes any prior universes row with the same name (cascading to all dependent rows) and writes the new universe in a single BEGIN/COMMIT transaction with batched inserts (500 rows per batch).

Gameserver consumption

The bang schema does not match the gameserver's domain schema. The gameserver expects Galaxy, Region, Cluster, Zone, Sector, Planet, Station, WarpTunnel, SpecialFormation, NPCRoster rows with multi-region scaffolding (Central Nexus, Terran Space, player-owned regions), faction-influence values, security/development/traffic levels, and the Sectorwars-specific nine-commodity catalog.

Per ADR-0069, bang emits a fully-formed region snapshot — clusters, formations, station and planet inventories, citadel content, and NPC rosters all arrive in the payload. The gameserver translator's job is therefore narrower than a content-derivation pipeline: it shape-checks the payload (_validate_universe_shape — required-key presence, version-prefix, sector-count) and structurally validates the translated plan (galaxy_validation.py, shipped 2026-08-05 — referential integrity, not per-formation placement invariants, re-corrected 2026-08-06 — see § Special formations), scaffolds the rows that depend on per-customer state (the Region row plus its operator config: subscription tier, governance, tax rate, language pack, aesthetic theme), glues the imported entities into the multi-region world (cluster faction-influence percentages, cross-region warp-gate wiring, Nexus attachment), and projects bang port commodities onto the gameserver's nine-commodity wire (canonical keys from bang's TRADING_PATTERNS; catalog defaults fill any omitted key — see bang_import_service.py). It does not derive clusters, stamp formations, or seed inventories — those arrive ready in the payload.

The translation algorithm — numbered steps with explicit per-entity translation tables, JSONB defaults, idempotency rules, and failure modes — is specified in ../SYSTEMS/bang-import-pipeline.md. All three operational paths below invoke the same translator pipeline; they differ only in how the bang payload reaches the translator and where the trigger comes from.

Path A — admin-triggered import

The admin endpoint POST /admin/galaxy/import-bang (see Swagger at <api-host>/docs) is the operator-facing trigger. The handler:

  1. Receives the admin's parameters (target region context, sector count, seed, density, faction balance, optional overrides).
  2. Invokes the generator out-of-process (CLI mode with the requested params), capturing the JSON.
  3. Hands the captured JSON plus the target region context to the translator pipeline.
  4. Returns the new Galaxy.id and Region.id to the admin.

Used for: post-launch region creation, admin-driven re-imports, and player-region provisioning. Because the import takes 60–120 seconds for a 5,000-sector Nexus, the admin endpoint runs the translator as an async background job and returns a job ID; the admin UI polls the job until completion.

Path B — bootstrap script

For first-time environment setup, the generator runs as a one-shot container that writes to a separate bang database (see "Service mode" above). A bootstrap script then reads bang.universes plus dependent rows, hands them to the translator pipeline, and exits. The script lives at services/gameserver/scripts/bootstrap_from_bang.py (target). It accepts a bang connection URL, a target Galaxy.name, and the target region context, and runs once per region during environment bring-up — typically twice on a fresh install (central_nexus then terran_space).

Used for: initial environment provisioning, full-galaxy re-rolls during major releases.

Path C — Alembic data migration

For test fixtures and CI environments, the generator output is checked in as a JSON artifact under services/gameserver/tests/fixtures/bang/ (target) and replayed via an Alembic data migration. The migration calls the same translator pipeline with the JSON loaded from disk. Suitable for deterministic tests; unsuitable for production-sized galaxies due to file size and migration time.

Used for: integration test fixtures, deterministic CI seeds.

Per-entity translation

The full per-entity translation (Sector, Warp, Port → Station, Planet, Nebula → Cluster nebula classification, NavHazard, SpecialLocation, fedspaceSectors, Cluster → Cluster, SpecialFormation → SpecialFormation, NPCRoster → NPCRoster) is documented in the appendices of ../SYSTEMS/bang-import-pipeline.md:

  • Appendix A — bang port commodity keys → gameserver nine-commodity wire (catalog defaults for any omitted key).
  • Appendix B — bang NebulaeType (2) → gameserver nebula classification (6) by zone bias and density.
  • Appendix C — bang PlanetType (6) → gameserver PlanetType enum.
  • Appendix D — bang SpecialLocationType → gameserver Sector special_features and associated rows.
  • Appendix E — bang Port class 0–8 → gameserver StationClass and StationType.
  • Appendix F — bang Cluster (named, sector-range-bounded) → gameserver Cluster row with seeded faction-influence overlay.
  • Appendix G — bang SpecialFormation → gameserver SpecialFormation row (1:1 type-and-properties copy; gameserver validates).
  • Appendix H — bang NPCRoster → gameserver NPCRoster row, with Phase 12.5c bootstrap_region hook running post-commit.

This operations doc does not duplicate the appendices; consult the SYSTEMS doc when implementing any of the three paths.

Reproducibility

Both modes are deterministic given a seed and a BigBangConfig. The same seed always produces the same universe regardless of when or where the generator runs. Operators wanting to reproduce a specific galaxy should record the seed and the full config from the universe row.

Versioning

The version field in the Universe and universes row is the generator schema version. The gameserver's translator must check this version against its supported-version allowlist and refuse to import an unknown one. Per ADR-0069, the contract is load-bearing: bang and the gameserver release in lockstep on contract bumps. Adding a field, 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.

Supported versions:

Version Status Notes
1.0.0 Legacy Original shape; no per-sector position field. Translator at bang-import-pipeline.md step 7 synthesizes 3D coordinates from a Hilbert-curve packing of sector-number ranges.
1.1.0 (and 1.1.0-pre.x pre-releases) Superseded Adds Sector.position: {x, y, z} integer fields scaled by 10000. Translator step 7 consumes position directly; no Hilbert packing. Walking-skeleton releases of sw2102-bang emit 1.1.0-pre.0 to flag that content layers (ports, planets, nebulae, special locations) may be stubbed.
1.3.4 Superseded Adds Warp.is_latent (dormant/latent warp edges) and explicit capitalSector / Sector.isCapital fields.
1.3.5 Superseded Adds the additive optional Port.black_market: boolean (default false), set on a fraction of Frontier-zone ports (~4% provisional, seed-reproducible) per ../FEATURES/economy/black-market.md. The gameserver import-map reads this explicit flag → StationType.BLACK_MARKET, graceful-on-absent — so a bang universe below 1.3.5 simply produces no black-market venues, no import error.
1.3.6 Superseded LEG-12: additive optional Sector.resources (asteroid fields) and SpecialLocationType value cabal_hq; cluster-type content biases. See ../SYSTEMS/bang-import-pipeline.md Appendix D.
1.3.7 Superseded LEG-47: TRADE_HUB port-class weights and POPULATION_CENTER planet-type / habitability bias (wire format unchanged; version bump signals generation-output shift).
1.3.8 Superseded LEG-48: densityFor(regionType) supplies default portPercent/planetPercent when omitted (see CLI / resolveConfig above). Wire format unchanged.
1.3.9 Superseded LEG-50: minimum hop spacing between special-formation anchors (5 hops; 3 hops when totalSectors ≤ 300 per ADR-0051 SK34). Wire format unchanged.
1.3.10 Superseded LEG-148: non-Nexus FRONTIER_OUTPOST clusters emit isDiscovered: false (generation.md:101 qualitative lower starting-discovery; no invented percentage). Nexus clusters unchanged. Wire format unchanged.
1.3.11 Current LEG-265: additive optional Port.dockingSlips four-class inventory (transient / longTerm / construction / specializedConstruction) per ../FEATURES/economy/docking-slips.md. TradeDock Tier-A/B construction rows deferred until Bang assigns tradedock tiers. Matches tip SCHEMA_VERSION in sw2102-bang/src/types.ts (a17b9e4).

Pre-release suffixes (1.1.0-pre.0, 1.1.0-pre.1, …) parse as >= 1.1.0 for the position-consumption rule but do not satisfy gameserver Phase 13 invariants that depend on content layers (e.g., #8 one TERRA at Capital, #9 two SpaceDocks). Pre-release imports are operator-only and require --allow-incomplete-content on the import endpoint (target).

BangGenerationJob lifecycle

Status: ✅ Shipped (Path A) — table bang_generation_jobs (migration a8d1f4e6c920), model BangGenerationJob / enum BangGenerationJobStatus, admin routes under /admin/galaxy/…, SSE log tail, startup orphan recovery. Documented from code 2026-08-05 (WO-CANON-DRAFT-BANG-GENERATION-JOB-LIFECYCLE).

Path A does not block the HTTP request on bang. The admin route inserts a job row, commits pre-created Region scaffolding, then schedules BangImportService.run_generation_job as a FastAPI background task. The row is the operator-visible source of truth for progress, logs, and failure.

States

Status Meaning
PENDING Queued. Row committed; background task not yet flipped to RUNNING (advisory lock / bang subprocess not yet acquired).
RUNNING Bang CLI subprocess(es) executing and/or translator writing. log_text is appended live; SSE clients poll/tail it.
COMPLETE Transaction committed successfully. Galaxy is ready. Sets completed_at, duration_ms, warnings_json.
FAILED Aborted — bang error, translator error, or orphan recovery. Sets completed_at + error_message. Pre-created Region rows named bang-{job_id}-{region_type} are reaped.

Terminal states: COMPLETE, FAILED. No resume/retry-in-place — operator queues a new job.

Transitions (who writes)

PENDING ──(BangImportService._set_job_status)──► RUNNING
RUNNING ──(_mark_job_complete)─────────────────► COMPLETE
RUNNING ──(_mark_job_failed)───────────────────► FAILED
RUNNING ──(startup orphan sweep, age >5 min)───► FAILED  (error_message="orphaned at startup")
  • Create → PENDING: POST /admin/galaxy/jobs (full 3-region bang), POST /admin/galaxy/{galaxy_id}/regions (additive player_owned), and related bang admin create routes in bang_galaxy.py. Always HTTP 202 with the job row.
  • PENDING → RUNNING / terminal: only BangImportService job helpers (_set_job_status, _mark_job_complete, _mark_job_failed).
  • Orphan recovery: on gameserver startup (main.py), any RUNNING row with started_at older than 5 minutes is force-failed. Index ix_bang_generation_jobs_status_started_at backs that query.

Row payload (operator-facing)

Column Role
params_json Serialized BangConfig — enough to re-run from history.
warnings_json List of {category, code, message, data?} from bang / translator validators.
log_text Concatenated bang stderr + translator progress; SSE endpoint streams this while RUNNING.
duration_ms / error_message Set on terminal states.
admin_user_id Operator who queued the job (ON DELETE RESTRICT).

Observability endpoints

  • List / get job: admin bang-galaxy job history routes (newest-first by started_at).
  • Live log: SSE tail over log_text while status is RUNNING (stops when the row leaves RUNNING).