Skip to content

0065 — Schema and data-model parity (Group K)

Status

Accepted.

Context

Sixteen audit findings on doc-vs-doc schema parity — places where ADRs and feature/system docs reference schema fields that don't yet exist in DATA_MODELS, or where DATA_MODELS docs reference columns nobody uses. Two critical (JSONB key drift, missing TradeDock columns); the rest are correctness-side cleanups. No new design decisions; this ADR ratifies a coordinated parity sweep.

Decision

M-J1 — Sector.defenses JSONB key: canonical drone_blocks

The canonical key for player-deployed drones in Sector.defenses JSONB is drone_blocks, per the formal schema at ../DATA_MODELS/jsonb-schema.md. The shorthand deployed_drones used in some docs (notably ../DATA_MODELS/pirate-holdings.md and ADR-0047) is non-canonical drift. Doc updates correct the key name in pirate-holdings.md; ADR-0047 stays immutable per ADR rules — its deployed_drones reference is now contextual / historical, not authoritative.

The combat resolver and pirate-holding-raid integrations read drone_blocks[*] per the formal schema. Code reads of the wrong key are doc-vs-code drift for the gameserver repo to resolve.

R-D1 — TradeDock schema columns added to stations.md

Per ADR-0041, three columns land on Station:

  • is_tradedock — Boolean, default false. True for the dedicated TradeDocks per Region (Frontier-zone trade-anchor pair per ADR-0041).
  • tradedock_tier — Enum nullable. gateway / industrial / luxury per the per-tier specialization table in ADR-0041. Null when is_tradedock = false.
  • region_assignment_role — Enum nullable. welcome / frontier_anchor / tradedock_a / tradedock_b per the per-region station-role table. Null for procedural stations without a designated role.

Documented in ../DATA_MODELS/stations.md.

R-I2 — engaged_pending_arrival NPC status

Per ADR-0042, npc_status enum gains an engaged_pending_arrival value: NPC has accepted an engagement assignment but is still en-route. Engagement-routing skips this NPC (already assigned); fleet engagement counts include it (committed to the fight). Documented in ../DATA_MODELS/npcs.md.

R-I3 — player_warp_knowledge table

Per ADR-0045, per-player warp discovery state lives in a PlayerWarpKnowledge table. New schema entry in ../DATA_MODELS/player.md:

  • (player_id, warp_id) composite key.
  • discovered_at timestamp.
  • discovery_source enum (personal_visit, warp_jumper_scan, corp_share, aria_hint).
  • corp_shared_at timestamp nullable — set when a corp-mate's discovery propagates.

The table is the source of truth for "what does this player know about the warp graph." Map-visibility queries read against it; ARIA reads it for hint generation per ADR-0045.

R-I5 — Region.pirate_ecosystem_state JSONB shape

The shape, per the existing ../SYSTEMS/pirate-ecosystem.md:

{
  "cleansed_at": "<iso8601 | null>",          // null until 7-day-zero-population threshold met
  "zero_population_since": "<iso8601 | null>", // when count of pirate-controlled holdings first hit 0
  "last_growth_tick_at": "<iso8601>",
  "suppression_modifier": <float 0..1>,        // 0.5 floor; 1.0 = no suppression
  "current_population_score": <int>,
  "target_population_score": <int>
}

Documented in ../DATA_MODELS/galaxy.md Region row.

M-G1 — Player.lumen_crystal_inventory ghost reference removed

Lumen crystals are team-pooled, not per-player. The Player.lumen_crystal_inventory reference in some docs is a ghost. Cleanup: remove the reference from any feature/system doc that mentions it; the canonical inventory lives on Team (per the team-treasury inventory schema in ../DATA_MODELS/player.md Team row).

M-G2 — Player.aria_assistance_level ghost reference

The correct field is PlayerTradingProfile.ai_assistance_level. References to Player.aria_assistance_level are ghosts. Doc fix: replace ghost references with the canonical path.

M-J2 — Station.defense_state.current_strength JSONB shape

The shape, anchored:

{
  "current_strength": <float 0..1>,    // 1.0 = full defense; 0.0 = stripped
  "last_damage_at": "<iso8601 | null>",
  "last_recovery_tick_at": "<iso8601>",
  "armor_layer_intact": <bool>,
  "shield_layer_intact": <bool>,
  "weapon_systems_online": <bool>
}

Documented in ../DATA_MODELS/stations.md.

R-O1 — ADR-0044 ARIA cluster-name integration

ARIA narrates cluster and formation names per ADR-0044. Cross-link added from the relevant ARIA doc (existing or ../OPERATIONS/aria.md) so ARIA's narration vocabulary surface is discoverable from both directions.

R-O2 — CentralNexusBank cross-reference gap

The PlayerCentralBankAccount schema in ../DATA_MODELS/player.md is referenced as design-only on Player but the cross-link to ../OPERATIONS/monetization.md (where the monetization-side flow lives) and ../SYSTEMS/region-lifecycle.md (where cascade deposits land) was missing. Cross-links added.

M-E1 — Ship.cargo JSONB extended with lumen_crystals + quantum_shards

The canonical Ship.cargo JSONB shape per ../DATA_MODELS/jsonb-schema.md gains keys for lumen_crystals and quantum_shards (mining drops per ../FEATURES/economy/mining.md). Both are integer counts; default 0; extracted at TradeDock for refining.

M-U1 — Orphan ARIA columns documented

Player.aria_blocked_until, Player.aria_violation_count, and Player.aria_trust_score were schema-only with no FEATURES/SYSTEMS surface. Per ADR-0057 these are load-bearing for the ARIA security model — the ADR-0057 doc updates already added the FEATURES/SYSTEMS references; this ADR confirms the cross-doc parity is closed.

M-U2 — PlayerTradingProfile feature-doc surface

PlayerTradingProfile is the per-player trading aggregation that ARIA reads. It currently has a schema entry in ../DATA_MODELS/player.md but no feature-doc surface. The aria.md ARIA doc references it; doc update adds an explicit subsection in ../OPERATIONS/aria.md under "Player model fields" pointing at the schema.

M-M1 — Migration fe22441146b1 idempotency

The migration adds Player.personal_reputation Integer default 0. Idempotency: the migration is safe to re-run because ALTER TABLE ... ADD COLUMN IF NOT EXISTS is the canonical pattern, and the default-0 backfill is deterministic. Document the idempotency note in the migration's docstring (gameserver-side concern; this ADR confirms the design intent).

M-M2 — Migration d2e3f4a5b6c7 defaults / backfill

The migration adds the ADR-0049 monotonic counter columns to Contract. Backfill: existing rows get partial_fulfilled_amount = 0 for cargo_delivery types and bulk_procurement rows whose quantity field has been wholly delivered get partial_fulfilled_amount = quantity. Document the backfill procedure in the migration's docstring.

Consequences

  • No code-side schema changes in this ADR. All 16 findings are doc-side parity work; the canonical schema spec aligns with what code already does (or what ADR-0041 / ADR-0042 / ADR-0045 already specified). Code-vs-doc drift identified by code-side reads (e.g., deployed_drones in code reading drone_blocks in spec) is for the gameserver repo to resolve per the repo's standard "code-vs-doc mismatches: leave alone" policy.
  • Doc surface grows by 5 new sections / table entries: TradeDock columns on Station; engaged_pending_arrival enum value; PlayerWarpKnowledge schema; pirate_ecosystem_state JSONB shape; Station.defense_state shape.
  • Ghost references purged in 2 places (M-G1, M-G2) — cleanups that prevent future code-spec drift.
  • Cross-link debt cleared (R-O1, R-O2, M-U1, M-U2): four doc pairs that referenced each other are now bidirectional.
  • Migration docstrings (M-M1, M-M2) are gameserver-repo concerns; this ADR confirms the design intent for those migrations is captured.

Alternatives considered

  • Pick deployed_drones over drone_blocks (M-J1). Rejected — drone_blocks is the richer structured shape with per-block ownership, faction, timestamp, hostility, weapon profile fields. deployed_drones is a shorthand that loses information.
  • Promote PlayerCentralBankAccount to a top-level OPERATIONS doc (R-O2). Rejected — schema lives in DATA_MODELS, behaviour lives in region-lifecycle.md, monetization-side context lives in monetization.md. Three cross-links is correct; a fourth top-level doc would duplicate.