Skip to content

0052 — Cross-System Contradictions (Batch 5)

Status

Accepted

Context

Three cross-system contradictions surfaced in the skeptical-audit pass (SK36, SK37, SK38). Each one is a small interaction between two existing systems where the rules don't compose without an explicit decision:

  • SK36 — Wreck creation per Ship.destruction_cause. ADR-0029 specifies WARP_GATE_ANCHOR suppresses wreck creation. The other destruction causes (COMBAT, HAZARD, SELF_DESTRUCT, ABANDONMENT_EXPIRED) have no explicit spec — implicit default is "create wreck" but never confirmed.
  • SK37 — Insurance × auto-bounty interaction. Owner files stolen report (places 50% auto-bounty); a hunter kills the stolen ship; the owner could collect insurance and the bounty refund, producing a profit on self-destruction. Open exploit potential.
  • SK38 — Region-termination cascade for player-built warp gates. Per ADR-0050 Batch 3, a region can terminate. A gate has two endpoints; if either endpoint's region terminates, the gate is broken. The cascade table marked this TBD.

These are tactical correctness fixes, not design pivots. Bundled into one ADR for working coherence — they all live in the destruction/lifecycle interaction layer.

Decision

SK36 — Wreck-creation matrix per destruction_cause

The _handle_ship_destruction flow (per FEATURES/gameplay/ships.md destruction-handler sequencing) consults a per-cause matrix to decide whether to spawn a CargoWreck:

destruction_cause Wreck spawned? Damage band for recovery roll Rationale
COMBAT Yes per killing weapon's damage type (per ADR-0007) Canonical. Standard recovery roll per the existing damage-type table.
HAZARD (black hole, radiation zone, warp storm) Yes kinetic band (50–70%) Environmental destruction; cargo plausibly survives in scattered debris. Treated as kinetic for the recovery roll.
SELF_DESTRUCT No n/a Intentional cargo denial. Self-destruct is the player's deliberate "deny enemies the cargo" act; spawning a wreck would defeat the mechanic.
ABANDONMENT_EXPIRED Yes kinetic band (50–70%) A ship un-claimed for 7 days past abandonment — cargo preserved by inactivity. Per ADR-0008 ship registry overhaul.
WARP_GATE_ANCHOR No n/a Hull consumed as gate focus per ADR-0029.

The matrix is checked in the _handle_ship_destruction step 3 short-circuit (per the canonical sequence in ships.md § Destruction-handler sequencing). Only WARP_GATE_ANCHOR and SELF_DESTRUCT short-circuit before insurance and wreck creation; all others proceed through the standard handler.

SK37 — Stolen-report mode choice (forfeit insurance OR forgo bounty)

When filing a stolen report, the owner explicitly picks one of two recovery vehicles. The choice is preserved on the report and propagates to the destruction handler.

Mode Auto-bounty placed? Insurance fires on destruction? Trade-off
with_bounty (default) Yes — 50% of ship value No Owner uses the bounty as their chosen resolution; the universe destroys the ship for them. Insurance is forfeit because the owner self-paid for the destruction.
no_bounty No Yes No bounty hunter incentive; the ship may stay stolen longer. Insurance fires normally if the ship is later destroyed by external hostiles.

The rule closes the collusion exploit: one recovery method per stolen-ship event, not both. Switching modes is permitted by retracting the stolen report and filing a fresh one (with the corresponding posting fee — no free toggle).

If the ship is destroyed but no stolen report was ever filed — or the report was filed in no_bounty mode — insurance fires normally per the standard ship-insurance flow.

Schema: Ship.stolen_recovery_mode enum (with_bounty / no_bounty) — set when stolen_status = True, cleared on retract. The destruction handler reads this value to decide whether to suppress insurance.

Endpoint signature (per SYSTEMS/ship-registry.md):

POST /api/v1/ships/{ship_id}/report-stolen
Body: { "recovery_mode": "with_bounty" | "no_bounty" }   // default "with_bounty" if omitted

SK38 — Region-termination cascade for player-built warp gates

When a region enters cleanup (per ADR-0050 Batch 3), the orchestrator processes player-built warp gates anchored to or from the region:

  1. Scan all WarpGate rows where source_sector_id or destination_sector_id is in the terminating region.
  2. Destroy both endpoints atomically — the entire WarpGate row (beacon + focus) deletes in one transaction. No half-gates left orphaned in either region.
  3. Notify the gate owner via realtime broadcast + ARIA narration: "Your warp gate {gate_name} has been destroyed — the {opposite_region_name} side terminated. A 50% construction-cost refund has been credited to your account."
  4. Pay compensation: 50% of the gate's construction cost (the originally-paid total at build time, as recorded on the WarpGate.construction_cost snapshot column) credited to the gate owner's Player.credits if they're online, or to their PlayerCentralBankAccount (per ADR-0050) if they're offline.

Why 50%, not 25%: the gate is destroyed by an event outside the owner's control. The construction cost includes the consumed Warp Jumper hull (1,000,000 cr equivalent) plus 50 Lumen Crystals plus other materials — a substantial investment. Half-refund keeps the loss real but doesn't punish the gate owner for someone else's subscription lapse. If the gate owner's own region is terminating, they're already going through their personal asset cascade (planets/stations/etc.) — gate compensation is independent of that.

Cross-regional edge case — gate with one endpoint in region A and the other in region B, both terminating in overlapping windows. The orchestrator that fires first scans, finds the gate, deletes the row, and pays the 50% refund. The second region's cleanup pass finds no gate to process. The owner is paid once.

Special case — gate owner has lapsed Galactic Citizen subscription: the 50% refund still credits to their account (Bank if no wallet access). The gate is destroyed regardless of the owner's subscription status; the refund is unconditional.

Consequences

Positive:

  • The wreck-creation matrix closes the implicit-default gap. Every destruction_cause has an explicit answer; the _handle_ship_destruction flow is fully deterministic without "what should we do for SELF_DESTRUCT?" guessing.
  • The stolen-report mode choice closes the insurance × auto-bounty exploit at the design level. Owner-collusion can no longer profit from self-destroying their own stolen ship — they pick one recovery vehicle at filing.
  • The gate cascade rule fills the last open hole in the Batch 3 termination cascade. Combined with ADR-0050, the entire region-termination flow now has explicit asset-disposition rules for every player-owned entity (ships, planets, stations, captured holdings, contracts, bounties, gates).

Neutral:

  • One new column on Ship: stolen_recovery_mode enum (nullable, set at stolen-report filing).
  • One new column on WarpGate: construction_cost snapshot (Integer, set at build commit; immutable thereafter — gate cost may be re-balanced post-launch but each gate's snapshot stays at its build-time value for the cascade refund calculation).
  • The wreck matrix is implemented as a constant lookup in the destruction handler — no schema change.

Negative:

  • A player who wants both bounty-hunter pressure and insurance peace-of-mind on a stolen ship has to pick. Acceptable: that's the exploit closure. If they value insurance, they file no_bounty; if they value the universe-driven destruction, they file with_bounty. Neither is wrong.
  • A 50% gate-construction-cost refund means the operator's economic model bears the other 50% — that's the cost of clean cascade UX. Acceptable: gates are rare strategic infrastructure, not high-volume; the cascade pays out infrequently.

Alternatives considered

SK36: implicit-default-yes for unspecified causes (rejected). Just say "all destruction_cause values create a wreck except WARP_GATE_ANCHOR." Rejected because SELF_DESTRUCT really shouldn't create a wreck — the act is intentional cargo denial, and a wreck defeats the mechanic. The explicit matrix is short and clear.

SK37: both fire, with insurance reduced by bounty cost (rejected). Insurance pays out but is reduced by 50% of ship value (the bounty cost). Owner net-recovers ~50% of ship value via insurance. Rejected because the math is fiddly and the cap-clamping behavior at low insurance tiers is awkward (basic insurance already covers <100%; subtracting bounty cost can go negative). The clean either/or rule is easier to reason about and explain.

SK37: insurance always suppresses on stolen-ship destruction regardless of mode (rejected). Rejected because it punishes legitimate no_bounty filers — they're trying to retain insurance coverage, and the mode-choice mechanism gives them that path explicitly.

SK38: 25% refund (rejected). Half the proposed value. Rejected because gates are large investments; 25% feels punitive when the destruction is involuntary.

SK38: 100% refund (rejected). Full repayment. Rejected because operator economics can't sustain "the operator pays full freight on every cascade-driven gate destruction." 50% is the balance.

SK38: orphan the gate (point to NULL endpoint) (rejected). Leave half a gate dangling in the surviving region. Rejected because it produces a confusing UX ("why does my gate go nowhere?") and creates schema-validity questions (can a gate row have NULL endpoints?). Atomic delete is cleaner.

SK38: transfer to an archival region (rejected). Move the gate to a designated archival region (e.g., the Central Nexus). Rejected because it preserves the gate's existence but breaks its in-fiction purpose (the gate was built to connect specific places); transferring it produces a confusing surface that doesn't match player intent.