Factions & Teams¶
Status: 🚧 Partial — faction reputation and team structures partially shipped; trade pricing consumes team aggregate standing via
get_trade_modifieron tip46bce720/ #752; ✅ port-access dock reputation gate consumes team standing viaresolve_effective_faction_standing_valueon tip46bce720/ #753 (LEG-814) — 🚧 mission-gate consumer remains tip-pending (Soft-HOLD #1305). (re-verified 2026-08-21 vs Sectorwars210246bce720.)
Two distinct social structures sometimes confused with each other:
- Faction — an NPC organization. You build standing with it through emergent behavior — combat, trade, exploration, social acts, resource flows; you do not "join" a faction and there are no quests to accept. Affects pricing, port access, patrol response, territorial reception.
- Team — a cooperative alliance of player accounts. You explicitly create or are invited to one; affects shared resources, coordinated combat, and treasury access.
A player has standing with every NPC faction simultaneously and is a member of at most one team.
Factions (NPC)¶
The seven designed factions¶
| Faction | Type | Theme |
|---|---|---|
| Terran Federation | Lawful Government | Order, security, prosperity |
| Mercantile Guild | Trade Consortium | Profit-neutral, infrastructure-heavy |
| Frontier Coalition | Independent Colonies | Anti-Federation autonomy |
| Astral Mining Consortium | Resource Corp | Monopolistic mining |
| Nova Scientific Institute | Research Org | Quantum / exotic tech focus |
| Fringe Alliance | Outlaw Coalition | Frontier rebels, smuggler refuge |
| Shadow Syndicate | Criminal Syndicate | Organized black-market trade |
| Galactic Concord | Operator-managed Authority (📐) | Central Nexus governance — fields the Nexus Sentinel Corps |
Two hostile-only entities — Pirates (random NPC raiders) and The Cabal (📐 Design-only endgame antagonist with a single fixed Headquarters in Terran Space) — cannot be allied with; only attacked. See ./faction-lore.md for full lore on all nine.
The Federation Police (Terran Federation enforcement arm — Marshal Interdictor squads in Federation Zones) and the Nexus Sentinel Corps (Galactic Concord enforcement arm in the Central Nexus) are both NPC-only police forces with special-issue Interdictor hulls (Federation Marshal Interdictor and Nexus Sentinel Interdictor respectively). They have their own engagement triggers, jurisdiction boundaries, and rep penalties for attacking. Full design in ./police-forces.md.
Reputation scale¶
Per-faction reputation runs −800 to +800, stored on Reputation.current_value. Two related views over the value coexist:
- The 17-tier
ReputationLevelenum (models/reputation.py) — setsReputation.current_level, the display title, and the value used by combat-response and port-access rules. - The 9 trade-modifier buckets (
faction_service.py:TRADE_MODIFIERS) — set the price multiplier at faction-controlled ports. Each bucket coarse-grains one or more enum tiers.
Both are derived from the same numeric value; the table below shows the boundaries and the price effect that applies in each band.
| Reputation value | Level enum | Trade-modifier bucket | Price multiplier |
|---|---|---|---|
| ≥ +700 | EXALTED | EXALTED | ×0.85 (15% discount) |
| +500 to +699 | REVERED (+700) / HONORED (+500) | REVERED | ×0.90 |
| +300 to +499 | VALUED (+400) / RESPECTED (+300) | HONORED | ×0.95 |
| +100 to +299 | TRUSTED (+200) / ACKNOWLEDGED (+100) | FRIENDLY | ×0.97 |
| +50 to +99 | RECOGNIZED | NEUTRAL | ×1.00 |
| −50 to +49 | NEUTRAL | NEUTRAL | ×1.00 |
| −51 to −99 | NEUTRAL (no enum boundary here) | NEUTRAL | ×1.00 |
| −100 to −299 | QUESTIONABLE (−100) / SUSPICIOUS (−200) / UNTRUSTWORTHY (−300) | UNFRIENDLY | ×1.05 |
| −300 to −499 | SMUGGLER (−400) | HOSTILE | ×1.15 |
| −500 to −699 | PIRATE (−500) / OUTLAW (−600) | HATED | ×1.30 |
| ≤ −700 | CRIMINAL (−700) / PUBLIC ENEMY (below) | PUBLIC ENEMY | ×1.50 |
The exact threshold function is faction_service.py:_calculate_reputation_level for the enum and TRADE_MODIFIERS (high-to-low list) for the modifier bucket.
Reputation triggers¶
Faction reputation is emergent only. There are no missions or quests to accept; rep moves through six trigger surfaces — combat, trade, exploration, social/political, ongoing-state drips, and resource flows — driven by natural play. The full set is canonical per ADR-0032.
Triggers are dispatched server-side through a single apply_emergent_action(player, action_type, sector_id, witnesses) wrapper that fans out to per-faction update_reputation calls in one transaction, applies the rivalry-cascade rule (below), and clamps results against the F2 decay ±100 floor/ceiling and combined-rep caps.
✅ Shipped — the apply_emergent_action(db, player, action, context) dispatcher (services/gameserver/src/services/emergent_reputation_service.py): a canon trigger table, fan-out via faction_service.apply_faction_rep_delta, and the rivalry cascade. Live call sites currently wire five triggers:
- Kill a Pirate or Cabal NPC → +5 Federation —
combat_servicekill resolver (KILL_PIRATE_NPC/KILL_CABAL_NPC; Cabal NPC spawn itself remains 📐 Design-only) - Buy BASIC / STANDARD / PREMIUM insurance → +2 / +5 / +10 Mercantile Guild — once per hull at
ship_upgradesinsurance purchase (BUY_INSURANCE_*) - First-scan a NEBULA or BLACK_HOLE sector → +15 Nova Scientific Institute —
movement_servicefirst-visit (NOVA_FIRST_SCAN_RESEARCH_SECTOR; ANOMALY / WARP_STORM still unrepresentable — noSector.typevalue) - Harvest Quantum Shards in a nebula → +1 Nova per 3 Shards —
quantum_serviceharvest (HARVEST_NEBULA_SHARDS_NS) - Build a public toll warp gate → Mercantile +30 / Frontier +5 / Nova +5 —
warp_gate_servicegate activation (BUILD_PUBLIC_WARP_GATE)
Remaining triggers below stay 📐 Design-only until their call sites invoke the dispatcher.
Per-faction trigger tables¶
Terran Federation (lawful order)
| Action | Δ | Notes |
|---|---|---|
| Kill a Pirate or Cabal NPC | +5 | combat resolver post-hook — ✅ Shipped (combat_service via KILL_PIRATE_NPC / KILL_CABAL_NPC); Cabal NPC spawn itself remains 📐 Design-only |
| Kill a player with active Wanted/Suspect status in Fed-Controlled space | +15 | combat hook reads wanted_status |
| Kill a system-bounty target in fedspace | +25 | bounty payout post-hook |
| Trade at a Federation-flagged port | +1 / 5,000 cr | re-tuned from +1/10,000 |
| Defend a Fed-Controlled sector (drone defense survives an attack) | +20 | re-tuned from +15 |
| Discover a sector inside or adjacent to Fed territory | +10 | re-tuned from +5 |
| Pay region tax cumulatively | +1 / 25,000 cr taxed | regional governance hook |
| Repel a planet siege on Fed-aligned colony | +50 | siege-lift event |
| Use the Federation distress beacon (one-way-stranding rescue) | −10 / use | 24-hour cooldown; free transport to nearest fedspace sector. The panic-button cost for being trapped behind a one-way warp. See ../galaxy/sectors.md#one-way-warps. |
Mercantile Guild (profit-neutral infrastructure)
| Action | Δ | Notes |
|---|---|---|
| Trade at any Guild-flagged station | +1 / 5,000 cr | core loop |
| Use a player toll gate (someone else's public gate, with cargo) | +1 / 5,000 cr cargo value | rewards trade-network participation |
| Successful arbitrage round-trip (≥30k profit, ≥3 sectors apart) | +5 | rewards skill, not just volume |
| Buy STANDARD insurance | +5 | one-time per hull — ✅ Shipped (ship_upgrades via BUY_INSURANCE_STANDARD) |
| Buy PREMIUM insurance | +10 | one-time per hull — ✅ Shipped (ship_upgrades via BUY_INSURANCE_PREMIUM) |
| Buy BASIC insurance | +2 | one-time per hull — ✅ Shipped (ship_upgrades via BUY_INSURANCE_BASIC) |
| Honor a public market-make order (post + fill within 24h) | +5 | reliability signal |
| Operate player port at fair tariff (5–6%) for 30 days | +20/month | per ../economy/port-ownership.md; legal fair band = tip MIN_TAX_RATE..FAIR_TARIFF_MAX (0.05..0.06) |
Frontier Coalition (anti-Federation autonomy)
| Action | Δ | Notes |
|---|---|---|
| Discover a sector ≥10 hops from any Capital | +15 | exploration core |
| First-arrival in an isolated cluster via Quantum Jump | +30 | trailblazer signal |
| Establish a colony in a Frontier-zone sector | +50 | their core mission |
| Tow a non-team-mate's drifting/escape-pod ship across ≥2 sectors | +15 | good-neighbor emergent |
| Trade at a Frontier outpost | +1 / 5,000 cr | volume |
Sell gourmet_food or luxury_goods to a Frontier colony port |
+2 / 10,000 cr | luxury-starved colonies |
| Defend a Frontier sector from pirates | +20 | defense |
| Vote in a Frontier-aligned regional governance resolution | +2 / vote | civic participation |
| Harvest in a nebula adjacent to AM-influence sector without an AM license | +5 / harvest | contested extraction; pairs with the AM penalty above — see ../galaxy/quantum-resources.md#faction-reputation-hooks |
Astral Mining Consortium (monopolistic mining — see ../economy/mining.md)
| Action | Δ | Notes |
|---|---|---|
Sell raw ore to an AM-flagged refinery |
+2 / 5,000 cr | double-weighted |
| Mine an asteroid-field sector with Mining Laser equipped | +1 / harvest cycle | per ../economy/mining.md |
| Pay AM claim-license fee voluntarily | +15 / license | legitimizes extraction |
| Defend an AM-claimed sector during siege | +25 | core archetype |
Trade precious_metals (Astral specialty) |
+1 / 8,000 cr | specialty preference |
| Harvest in a nebula adjacent to AM-influence sector without an AM license | −5 / harvest | unlicensed extraction; pairs with FC entry below — see ../galaxy/quantum-resources.md#faction-reputation-hooks |
Nova Scientific Institute (research)
| Action | Δ | Notes |
|---|---|---|
| First-scan a NEBULA / BLACK_HOLE / ANOMALY / WARP_STORM sector | +15 | research data — ✅ Shipped for NEBULA / BLACK_HOLE (movement_service first-visit via NOVA_FIRST_SCAN_RESEARCH_SECTOR); ANOMALY / WARP_STORM unrepresentable (no Sector.type) |
| Harvest Quantum Shards in a nebula | +1 / 3 Shards | per ../galaxy/quantum-resources.md — ✅ Shipped (quantum_service harvest via HARVEST_NEBULA_SHARDS_NS) |
| Harvest Quantum Shards in a Crimson nebula specifically | +1 / Shard | 4× rate; Nova prizes high-field-strength data — 📐 Design-only (no distinct call site; general harvest uses the +1/3 path) |
| Refine 5 Shards → 1 Crystal at a Nova-flagged station | +8 / job | highest of the refining venues |
Sell exotic_technology to a Nova-flagged station |
+2 / 5,000 cr | research material |
| Run a long-range Quantum Jump scan (paid scan, 5 turns) | +1 / scan, capped 5/day | their tech, their data |
| Donate xeno-artifact to Nova station | +30 | one-shot rare event |
| Sell anomaly-investigation dataset to Nova | +5 / dataset | data is a commodity |
| First-scan a Genesis-formed planet within 7 days of formation | +15 | Genesis biology |
Fringe Alliance (smuggler coalition)
| Action | Δ | Notes |
|---|---|---|
| Black-market transaction at a Fringe-controlled port | +25 / transaction | per ../economy/black-market.md |
| Successfully evade a Federation contraband scan with cargo | +10 / evasion | anti-symmetric to Fed's scan-survive trigger |
| Trade at a Fringe-controlled port (legal goods) | +1 / 5,000 cr | volume |
| Pilot a stolen ship into Fringe space and dock at a Fringe port | +5 | one-shot per stolen-ship session |
| Survive a Suspect Status cycle (1+ hour) | +15 | one-shot per cycle |
| Tow a Fringe-flagged smuggler out of Fed pursuit | +20 | rescue mechanic |
| Pay tribute to a Pirate (the "pay" branch of the encounter) | +2 | refusing to fight is its own statement |
Shadow Syndicate (criminal trade — see ../economy/black-market.md#syndicate-fence-venues)
| Action | Δ | Notes |
|---|---|---|
| Fence a Cargo Wreck cargo at a Syndicate fence venue | +5 / 5,000 cr fenced | core loop |
| Sell stolen-flagged commodity (origin = early-grace salvage) | +10 / transaction | flagged-origin path |
| Launder credits through ≥3 trade hops (laundering chain completes) | +20 / laundering completion | per black-market spec |
| Survive a full Wanted Status cycle without impound | +10 | one-shot per Wanted cycle |
| Forge port-tariff records (skim ≥1% as port owner) | +20 / month | tariff-evasion event |
| Place player-bounty on Heroic+ player (frame-job) | +10 / placement | reads target's personal-rep tier |
Pirates (hostile-only — capped at 0)
| Action | Δ Pirates | Notes |
|---|---|---|
| Pay tribute to a Pirate | +2 (toward 0) | unchanged from canon |
| Spare a Pirate at <25% hull (let them flee) | +5 (toward 0) | mercy path |
| Sell to a Class-8 (Black Hole) port — pirate fence chain | +1 / 5,000 cr (toward 0) | economic engagement clambers out of deep negative |
Negative emergent triggers (selection)¶
| Action | Affected | Δ |
|---|---|---|
| Attack a faction-flagged ship | host faction | −50 |
| Attack a faction-controlled port | host faction | −100 |
| Smuggling violation in faction-policed sector | policed faction | −25 |
| Hold a black-market commodity passing through Fed Core (drip) | Federation | −10 / sector hop |
| Mine in Astral-claimed sector unlicensed | Astral | −10 / extraction |
| Detonate Genesis Device in NEBULA sector | Nova | −50 |
| Genesis-deploy in Federation core without permit | Federation | −100 |
| Turn in a Fringe operative for bounty | Fringe | −100 + permanent betrayal flag |
| Sustained Heroic personal rep (≥250) for 7+ days | Fringe | −5 / day drip |
| Sustained low personal rep (≤−250) for 7+ days | Mercantile | −2 / day drip |
| Predatory tariff (≥7%) on owned port for 7+ days | Federation, Mercantile, Frontier | −15 / week each |
| Pod-kill anywhere visible to fed-space sector presence | Federation | −25 |
| Capture a faction-owned planet | owning faction | −50 (📐 NO-CANON, dormant — see note below) |
📐 Capture a faction-owned planet is defined but unwired. emergent_reputation_service.PLANET_CAPTURE_FACTION_PENALTY = -50 and its apply_planet_capture_faction_penalty helper exist and are ready to fire, but the Planet model has no faction-owner field today (planets are owned only by human Players via player_planets, or unowned) — the combat-side caller never resolves an owning faction, so this trigger has never fired in practice. This row is not in ADR-0032's ratified action set; −50 is a NO-CANON proposal mirroring the existing attacked_chartered_planet personal-rep penalty (also −50), picked as "the smallest sensible intervention until canon lands" (per the code's own comment). Activates automatically once planet faction-ownership ships — no additional wiring needed on this side.
Rivalry cascade rule¶
When Δ_X > 0 to a rivalry-locked faction X, an automatic fractional negative fires to X's canonical rival in the same transaction. Cascades fire on positive deltas only — negative actions do not reward rivals (that path is farmable). Cascades respect F2 decay floor/ceiling and the combined-rep caps.
| Source faction | Rival | Cascade fraction |
|---|---|---|
| Terran Federation ↔ Fringe Alliance | each direction | 0.5× |
| Mercantile Guild ↔ Shadow Syndicate | each direction | 0.5× |
| Frontier Coalition ↔ Astral Mining | each direction | 0.4× (📐 — promoted to combined-rep cap 700) |
| Nova Scientific Institute ↔ Astral Mining | each direction | 0.4× (📐 — promoted to combined-rep cap 700) |
| Federation → Pirates | one-way | 0.2× (📐 — one-way Pirate cascade) |
| Fringe → Pirates | one-way | +0.1× (clamps at 0; never positive) (📐 — one-way Pirate cascade) |
✅ Shipped — the dispatcher computes the cascade on positive deltas and applies the two fully-seeded 0.5× pairs (Terran Federation ↔ Fringe Alliance, Mercantile Guild ↔ Shadow Syndicate); the rows flagged 📐 are registered but not yet live (await the combined-rep cap / Pirate-cascade machinery).
Anti-symmetric matrix (selected multi-faction events)¶
Some actions move multiple factions simultaneously beyond the rivalry cascade. Fired by the same apply_emergent_action wrapper as a single transactional event.
| Action | TF | MG | FC | AM | NS | FA | SS | PI |
|---|---|---|---|---|---|---|---|---|
| Kill Pirate in Fed-Controlled sector | +10 | 0 | 0 | 0 | 0 | −5 | 0 | −10 |
| Refine Shard at Nova facility | +2 | 0 | 0 | −2 | +8 | 0 | 0 | 0 |
| Refine Shard at Syndicate shadow venue | −3 | −5 | 0 | 0 | −2 | +1 | +6 | 0 |
| Smuggle through Fed (evade scan) | −25 | −5 | +1 | 0 | 0 | +10 | +5 | +1 |
| Smuggle through Fed (caught) | −150 | −10 | 0 | 0 | 0 | +10 | +5 | +5 |
| Genesis-deploy in Frontier zone | −10 | 0 | +75 | −10 | +5 | +5 | 0 | 0 |
| Build a public toll warp gate ✅ | 0 | +30 | +5 | 0 | +5 | 0 | 0 | 0 |
| Build a private/whitelist warp gate | −5 | 0 | +5 | 0 | 0 | +5 | +10 | 0 |
| Tow a non-team-mate home | +2 | +5 | +15 | 0 | 0 | 0 | 0 | 0 |
| Buy PREMIUM insurance ✅ | 0 | +10 | 0 | 0 | 0 | 0 | 0 | 0 |
| Donate xeno-artifact to Nova | +5 | 0 | +2 | −10 | +30 | 0 | 0 | 0 |
| Pay pirate tribute | −5 | −2 | −2 | 0 | 0 | +2 | +1 | +2 (toward 0) |
✅ on a matrix Action cell = live apply_emergent_action call site (BUILD_PUBLIC_WARP_GATE at gate activation; BUY_INSURANCE_PREMIUM at hull insurance purchase). Unmarked rows remain 📐 Design-only.
Ongoing-state drip mechanics¶
Several rep effects are drips on persistent states rather than one-shot events. Per-tick updates are accumulated and flushed hourly by the wrapper.
| State | Effect |
|---|---|
| Wearing a faction-issued ship skin in issuing-faction territory | +1/hr issuing faction |
| Wearing a faction-issued ship skin in rival territory | −2/hr rival faction |
| Carrying contraband openly through a Fed-Controlled sector | −1/sector hop Federation |
Sustained personal_reputation ≥ +250 (Heroic+) for 7+ days |
−5/day Fringe |
Sustained personal_reputation ≤ −250 (Outlaw+) for 7+ days |
−2/day Mercantile |
| Active Wanted Status, docked at a Fringe port | +1/hr Fringe (welcomes fugitives) |
Refinery-of-record provenance¶
📐 Design-only. Every Quantum Crystal carries a refined_at_faction_id provenance tag. Using a Crystal in a warp gate Phase 1 commit (per ADR-0029) awards +25 to the refining faction at gate-activation. This single hook makes refining venue choice load-bearing for the late-game arc — the Crystal is anonymous fungible economically but politically remembers where it was minted.
Witness mark¶
📐 Design-only. When a faction patrol fights in a sector and the player neither engages nor flees, a neutral witness mark accrues against that patrol. Three witness marks at the same patrol convert to +5 of that faction. Rewards "being there" without role-play cosplay; ties peripheral participation to faction identity.
Per-day throttle¶
To prevent farming, each (player, faction) pair caps at 10 events/day. Once the cap is hit, that faction's positive award is skipped entirely for the rest of the day (a full stop rather than ADR-0032's half-rate continuation). Implemented at the apply_emergent_action dispatcher before write.
✅ Shipped — the per-(player, faction) 10-events/day cap (full-stop variant) is enforced in emergent_reputation_service.apply_emergent_action on positive direct deltas, with counters held in player.settings JSONB under emergent_rep_throttle (no migration).
Global daily pool (per ADR-0056 N-V1). On top of the per-faction cap, each player has a single daily pool that aggregates the sum of positive faction-rep deltas across all factions. Launch value: 100 rep / day. When the pool is exhausted, further faction-rep gains drop to 0 for the rest of the day (the action itself still applies — cargo delivered, NPC killed — but the rep delta is zeroed). Negative deltas are not throttled. The pool resets at UTC midnight.
✅ Shipped — the 100-rep/day global pool clamps each positive award to the pool remaining; negatives never consume the pool or a per-faction event count; the bucket carries its open-date and resets when the stored UTC date is no longer today.
The two redemption-arc actions in the cascade-recovery section below are the only canonical bypasses of the global pool.
Additive amendment (per ADR-0091 §8 Amendment A): the same ADR-0056 detection substrate also gates planet-settlement contests via the settle_contest surface — a HARD-flagged cluster member cannot contest (settle) a planet. See ../../OPERATIONS/multi-account-detection.md.
These numbers are launch balance targets; the underlying API (faction_service.update_reputation(player_id, faction_id, change, reason)) accepts an arbitrary signed integer, so live tuning happens by adjusting the values that game code passes in, not by changing the schema.
Cascade lockout — redemption arc¶
Per ADR-0056 N-F2. A player who has drifted into deep Pirate alignment (via the rivalry-cascade rule above) needs a discrete break-out path back toward Federation. Two new entries in the action-delta table provide it:
| Action | Faction-rep delta | Notes |
|---|---|---|
| Voluntarily destroy a captured pirate Stronghold or Outpost | +500 Terran Federation, −500 Pirates | Sacrifices the captured holding (no salvage, no compensation). Clears Pirate-alignment cascade lockout. One-shot per captured holding; can be repeated across multiple holdings. Bypasses the global daily pool — this is the recovery vehicle, the cap would defeat the purpose. |
| Donate the wealth from a captured pirate holding to Federation | +200 Terran Federation, −100 Pirates | The captured holding's stored credits + commodity inventory transfer to Federation; the holding itself remains the player's. Subject to the daily pool cap. |
The player must own (have captured) a pirate holding per ADR-0047 and ADR-0048 to use either action — these are not paths tourists or new accounts can stumble into. ARIA narrates these options when the player's faction-rep enters cascade-lock state, so the path is discoverable without a dedicated UI surface.
Faction-rep ↔ personal-rep — disjoint scopes¶
Per ADR-0056 N-D1. Faction reputation (this section) and personal reputation (per ./ranking.md) are independent signals. They do not cross-feed unless explicitly enumerated. A single in-game action can carry both a faction-rep delta and a personal-rep delta — they apply in parallel inside the same transaction; neither dampens or amplifies the other.
Cross-feeds that do exist (Launch list):
| Source | Trigger | Cross-feed |
|---|---|---|
| Personal → Faction | Entering Wanted Status | −50 rep with all Federation factions; +25 rep with all Pirate factions; one-shot at the Wanted-Status state-edge, not per-action |
| Personal → Faction | Wanted Status clears | No reverse cross-feed; the original deltas are not refunded |
Future cross-feeds must land in this table via a new ADR.
Reputation decay¶
Reputations outside the ±100 band decay back toward that band when the player has not interacted with the faction for over 30 days:
- Inactivity threshold: 30 days since
Reputation.last_updated. - Decay rate: −1 point per inactive day past the threshold.
- Per-call cap: max 50 points of decay applied in a single
apply_reputation_decayinvocation. - Decay floor / ceiling: positive reputations decay toward +100 (never below); negative reputations decay toward −100 (never above). The −99 to +99 neutral band is preserved as-is — values inside it never decay.
- Pause flag:
Reputation.decay_paused(admin lock) andReputation.is_lockedskip decay entirely.
Each decay event appends to Reputation.history JSONB with reason: "Inactivity decay (<N> days idle)" for audit.
Rivalries¶
FACTION_RIVALRIES defines combined reputation caps:
- Terran Federation ↔ Fringe Alliance — combined max 800.
- Mercantile Guild ↔ Shadow Syndicate — combined max 600.
Gaining standing with one faction effectively caps how high you can climb with its rival.
✅ Shipped — the combined-rep cap on positive gains is enforced in apply_emergent_action via a synchronous replica of FactionService._apply_rivalry_cap reading the same FACTION_RIVALRIES table; the two fully-seeded pairs above are live, and a positive delta is clamped so the rival pair never exceeds its combined ceiling.
📐 Design-only — additional rivalries (per ADR-0032) promoted to combined-rep caps:
- Frontier Coalition ↔ Astral Mining Consortium — combined max 700.
- Nova Scientific Institute ↔ Astral Mining Consortium — combined max 700.
Territory & influence¶
Each sector's relationship to a faction is described by an influence percentage (0–100) on the per-(sector, faction) SectorFactionInfluence row. The percentage drives a four-tier territory taxonomy used by patrol and pricing logic:
| Territory state | Influence | Behavior |
|---|---|---|
| Core | 100% | Faction HQ and immediate vicinity. Patrols guaranteed; hostile-reputation players attacked on sight. |
| Controlled | ≥ 75% | Faction port presence; frequent patrols; minor penalties for hostile reputation. |
| Contested | 40–60% across two or more factions | Mixed patrols; PvP-friendly; port prices follow the dominant-influence faction's reputation rule for the player. |
| Uncontrolled | 0% | Pirate spawn zones; no faction benefits or penalties. |
Dynamic influence¶
Player actions adjust influence. Targets — design balance, may be tuned at launch:
| Action | Effect | Tip status (46bce720) |
|---|---|---|
| Defend a faction-flagged sector (drone defense survives an attack) | +1% influence for that faction | ✅ tip-shipped — apply_defense_survived_sector_influence (faction_service) hooked from combat_service drone-defense survival (PR #770) |
| Destroy a rival-faction ship | −2% rival influence | ✅ tip-shipped — apply_rival_kill_sector_influence (faction_service) hooked from combat_service ship-destroy resolution (PR #770); unit test_kill_defend_sector_influence |
| Build a warp gate connecting two sectors | +5% influence in the connecting sector for the gate-builder's dominant-rep faction | 📐 Design-only — no tip call site |
| Establish a colony in-sector | +3% influence for the player's dominant-rep faction | 📐 Design-only — no tip call site |
| Open a player port at fair tariff (5–6%) for 30 days | +2% influence for the dominant-faction of the host sector | 📐 Design-only Soft-HOLD — Dynamic +2% influence call site not tip-ancestor (PR #762 Soft-HOLD; do not invent); fair band = tip MIN_TAX_RATE..FAIR_TARIFF_MAX |
Influence effects¶
- Port prices swing ±10% based on the dominant-faction reputation of the player.
- Patrol spawn rate scales with influence — higher influence means more friendly patrols.
- Pirate spawn rate is inversely proportional — high faction influence suppresses pirate encounters.
- Emergent rep triggers in high-influence sectors weight toward the dominant faction (kills, defenses, discoveries credit the dominant influencer; secondary influencers ≥25% receive half-share rep).
🚧 Partial — SectorFactionInfluence schema + write path (faction_service.adjust_sector_influence) are on the feature tip; kill −2% / defend +1% Dynamic influence call sites tip-shipped on 46bce720 / #770; ADR-0021 read-side (territory taxonomy via sector_territory_tier, patrol_spawn_weight derivation + Loop B read, UTC idle daily decay) is implemented on LEG-INI-05 branch / pending merge (6d42bf2c / PR #603 — open as of 2026-08-20, tip_HAS_land=NO vs 46bce720; Fibril Soft-ORDER #1634 / LEG-1558 land path — not tip-shipped). Canonical schema + formulas: ../../DATA_MODELS/gameplay.md#sectorfactioninfluence (LEG-74).
📐 Design-only residuals:
- Real Faction.base_patrol_intensity / zone_mod columns (LEG-INI-05 tip uses identity defaults 1.0 × 1.0)
- Remaining unwired Dynamic influence action→delta call sites from the table above: warp-gate +5%, colony +3%, fair-tariff streak +2% (kill −2% / defend +1% are not in this residual — tip-shipped on 46bce720 / #770)
- Influence-effects prose beyond derived patrol_spawn_weight / taxonomy (port-price swing, pirate-spawn inverse, emergent-rep weighting in high-influence sectors)
Reputation gating — code cross-index¶
Canon describes faction reputation as one mechanic; gating consumers are deliberately split across independent services (DECISIONS.md faction-rep-gating-cross-index-not-consolidate, 2026-08-04). Do not consolidate them into one gate helper — keep this table current when a new consumer ships so drift is visible.
| Surface | Gate | Service / entry |
|---|---|---|
| Station docking / slip access | Faction tier vs station threshold | docking_service.check_reputation_gate |
| Takeover / station purchase | Trusted+ (Heroic+) buyer tier | port_ownership_service buyer-tier gate |
| Warp-gate traversal | Per-gate faction_rep_min / faction_rep_max |
warp_gate_service._check_faction_rep_layers |
| Black-market / contraband | Fringe (OUTLAWS) ≥ RECOGNIZED | contraband_service._passes_rep_gate |
| Faction-issued bounty collect | Issuing-faction tier ≥ gate level | bounty_service collector faction gate |
| Genesis device acquire/deploy | Federation reputation bar | genesis_service / api/routes/genesis |
| Tradedock construction access | Guest vs full-access thresholds | construction_service Task B-2 gates |
| Ship-upgrade Exalted catalog | Faction-tier ship requirements | ship_upgrade_service / api/routes/ship_upgrades |
Emergent award path (deltas, throttle, rivalry cascade) stays centralized in emergent_reputation_service.apply_emergent_action — that is the write rail; the table above is the read/gating rail.
Source map¶
| Topic | Path |
|---|---|
| Faction service & rivalries | services/gameserver/src/services/faction_service.py |
| Faction model | services/gameserver/src/models/faction.py |
| Reputation model | services/gameserver/src/models/reputation.py |
| Emergent award rail | services/gameserver/src/services/emergent_reputation_service.py |
| Rep-gating consumers | see Reputation gating — code cross-index |
| API routes | services/gameserver/src/api/routes/factions.py |
Teams (player)¶
Creation and membership¶
- Creation cost: 10,000 credits (
TEAM_CREATION_COSTinservices/team_service.py). - Default size cap: 4 members (
max_membersparameter — configurable per team; the canonical balance target is 4). - One team per player: enforced at creation and invite acceptance.
- 24-hour leave cooldown between teams (design).
Roles¶
models/team_member.py:TeamRole defines four roles:
LEADER— full control: dissolve team, transfer leadership, assign all roles, override any permission. Exactly one per team (resolved viaPlayer.team_idof the leader).OFFICER— invite, kick, manage treasury (subject to permission flags), manage alliances and faction-aligned operations.MEMBER— standard membership; treasury deposit, default vision sharing, no admin actions.RECRUIT— probationary; deposit-only treasury access, no invite/kick rights.
Granular per-member permissions sit on TeamMember as a mix of explicit booleans (can_invite, can_kick, can_manage_treasury, can_manage_alliances) and a free-form permissions JSONB column for future extensions.
Treasury¶
Shared resource pool with 12 resource types:
Credits and the canonical 7-commodity catalog (per ../definitions.md#resource-types): Ore, Organics, Gourmet Food, Fuel, Equipment, Exotic Technology, Luxury Goods. Strategic resources also tracked: Quantum Shards, Quantum Crystals, Lumen Crystals, Prismatic Ore.
Operations: deposit, withdraw (permission-gated), transfer between members — credits and quantum_crystals only (PLAYER_TRANSFERABLE_RESOURCES whitelist); the other 10 resource types can be held in the treasury (server-fed, e.g. loot) but are not player-movable — plus transaction history across all 12 types.
Status: ✅ Deposit / withdraw / transfer + balance are shipped and live-proven (2026-06-14, see FINDINGS.md) — wired to the player-client team Treasury tab. ✅ Transaction history is a dedicated ledger: each treasury movement writes a
TreasuryTransactionrow to theteam_treasury_transactionstable (services/gameserver/src/models/treasury_transaction.py), capturingteam_id,resource_type,kind,amount,balance_after,actor_player_id,reason, andcreated_at. Corrected 2026-08-04: the headline above previously read "transfer between members" across all 12 types, contradicted by this same caveat two paragraphs later — headline now states the credits/quantum_crystals-only scope directly.
Combat advantages¶
- 60/40 statistical edge against non-team opponents (design).
- Friendly-fire prevention. ✅ Shipped — enforced pre-lock on ship-to-ship (
attack_player) and fleet battles (initiate_battle), plus same-team rejection on planet / sector-drone / warp-gate / port attack entrypoints (CombatService._is_same_team, WO-FIX-FRIENDLY-FIRE-LOCK-PARTIAL-COVERAGE + attack_port residual). - Shared sector vision.
- Coordinated drone deployment.
- Defensive notifications when any teammate is attacked.
Fleet operations¶
A team can field fleets — multi-ship formations for coordinated battle. See combat.md for fleet mechanics.
Team reputation with factions¶
Team has a calculated faction-standing using one of three methods: - Average (default). - Lowest member's standing. - Leader's standing.
Switching method has a 7-day cooldown. Faction interactions treat the team as a unified diplomatic entity.
Status: ✅ Compute + expose + notify are shipped and live-wired (WO-RT-TEAM-REP). ✅ Trade pricing consumes team standing on tip
46bce720/ #752 —FactionService.get_trade_modifierresolves viaresolve_effective_faction_standing_value(teamTeamReputationaggregate when the player has ateam_id; otherwise personalReputation). ✅ Port-access dock reputation gate (docking_service.check_reputation_gate) resolves via the same helper on tip46bce720/ #753 (LEG-814). 🚧 Mission-gate consumers of team standing remain not yet built (Soft-HOLD #1305) — do not read pricing+port-access land as universal interaction coverage.
Source: services/team_reputation_service.py; api/routes/teams.py (GET /teams/{team_id}/reputation, PUT /teams/{team_id}/reputation-method); schema on TeamReputation (models/reputation.py).
Methods — _aggregate_value per faction, over each team member's Reputation.current_value for that faction (a member with no row contributes NEUTRAL (0), matching Reputation's own default):
- Average (default) — round(mean(member_values)).
- Lowest — min(member_values).
- Leader — the team leader's own value for that faction (NEUTRAL (0) if the leader has no Reputation row).
- An empty team (zero members) degrades to NEUTRAL (0) under every method.
Recalculation — recalculate_team walks the full faction catalog every run (not just factions a member has interacted with), so a team's faction_reputation snapshot always has a complete, predictable shape. Each faction's aggregated value is mapped to the same 16-tier ReputationLevel ladder (EXALTED … CRIMINAL/PUBLIC_ENEMY) faction_service uses for individual players. A tier change since the prior snapshot appends to pending_notifications and fires a team_reputation_changed realtime event to the team's websocket room; the very first computation for a faction is never itself treated as a "change." [NO-CANON] the recalculation cadence itself — canon states only that a standing exists and updates, not how often; the service defaults to daily (RECALCULATION_INTERVAL), flagged for DECISIONS.
Method switch (PUT /teams/{team_id}/reputation-method, team leader only) — validates the requested method, enforces the 7-day cooldown against the most recent method_switch entry in TeamReputation.history (a 409 with retry_after if still cooling down), records the switch, and forces an immediate recalculation on success.
Read path (GET /teams/{team_id}/reputation, team members only) — lazily creates a team's TeamReputation row on first read (starts on AVERAGE, due for recalculation immediately) and recalculates inline if the stored snapshot is past next_recalculation, so a fresh team's standings self-heal instead of sitting empty. This read-triggers-refresh path remains a [NO-CANON, pragmatic interim] self-heal for stale snapshots. The scheduler sweep counterpart (sweep_due_team_reputations, gated behind a TEAM_REPUTATION_SWEEP_LOCK_KEY advisory lock) is live-wired: _run_team_reputation_sweep_sync in scheduler/reputation_team_sweeps.py invokes it, and the NPC scheduler core loop calls that helper each tick (scheduler/core_loop.py; re-exported via npc_scheduler_service). [NO-CANON] sweep/recalculation cadence itself is still flagged — not ratified by this wiring-status correction.
[RESOLVED 2026-08-04 — DECISIONS team-reputation-calculation-method-canonical / 093c38ee] — TeamReputation.calculation_method is the single source of truth. Team.reputation_calculation_method is deprecated (comment-only; column retained pending a Max-gated DROP). The service never reads or writes the Team-level column.
War system (partial)¶
🚧 Partial — declarations, PvP-kill scoring, ceasefires, threshold-based victory, and the provisional per-member credit payout are shipped on the gameserver. Player-client war UI is ✅ Implemented on LEG-73 tip / pending merge (2dbd7773 / PR #606 — open, not yet on feat/new-feature-development). Reputation and other non-credit victory rewards remain unbuilt.
Source: api/routes/teams.py (declare_war/list_wars/ceasefire, /{team_id}/wars/*); services/team_war_service.py (record_pvp_kill, _apply_victory_payout, the resolution loop); player-client TeamWarPanel on LEG-73 tip (PR #606).
Declare (POST /teams/{team_id}/wars/declare, team leader only) — records a mirrored active_wars entry in member_roles JSONB on both teams ({target_team_id, declared_by, declared_at, reason, status: "active", score: {us: 0, them: 0}}). Cannot declare on your own team or on a team you're already at war with. Both team rows are locked in ascending-id order to avoid an AB-BA deadlock against fleet_service's concurrent-battle team locks.
Scoring — team_war_service.record_pvp_kill fires on a resolved PvP ship-kill. If attacker and defender are on different teams that hold a mutual status: "active" war entry, it increments the kill count on both sides' JSONB entries (mirrored: attacker's us == defender's them). Never raises into combat resolution (defensive wrapper at the call site).
Victory — the first side to reach VICTORY_KILL_THRESHOLD = 10 kills auto-flips both war entries to status: "ceased", cease_reason: "victory", with winner_team_id/loser_team_id recorded. The 10-kill threshold is a NO-CANON provisional number (team_war_service.py:37) — this doc previously gave no kill threshold at all, so a concrete gate was needed to let wars end without a manual ceasefire; it awaits DECISIONS.md ratification, same as the other unratified magnitudes this session (loot-mint band, surplus-pioneer rate).
Victory payout — on threshold victory, _apply_victory_payout credits each living member of the winning team VICTORY_PAYOUT_PER_MEMBER = 5000 credits. The 5,000cr-per-member magnitude is a NO-CANON provisional number (team_war_service.py:38) — same ratification class as the 10-kill threshold above; it awaits DECISIONS.md ratification. No reputation or other non-credit victory rewards are paid.
Ceasefire (POST /teams/{team_id}/wars/ceasefire, leader of either involved team) — manually flips a mutual active entry to status: "ceased" without requiring victory.
List (GET /teams/{team_id}/wars, optional ?status=active|ceased filter). ✅ Shipped — WarEntry response_model exposes cease/victory fields (ceased_at, ceased_by, cease_reason, winner_team_id, loser_team_id, victory_at) so persistence is not stripped at the API boundary (PR #609 MERGED → 0f35af32, tip-ancestor on 46bce720).
Player-client UI — ✅ Implemented on LEG-73 tip / pending merge: TeamWarPanel (declare / list active|ceased / ceasefire / victory rendering) wired into TeamManager and the Crew dossier — PR #606 @ 2dbd7773 (open, not yet on feat/new-feature-development).
Not built: reputation or other non-credit victory rewards for the winning team.
Alliances (design only)¶
📐 Design-only — multi-team coalitions with shared vision, no-fire pacts, council voting.
Source map¶
| Topic | Path |
|---|---|
| Team service | services/gameserver/src/services/team_service.py |
| Team reputation service | services/gameserver/src/services/team_reputation_service.py |
| Team war service | services/gameserver/src/services/team_war_service.py |
| Team model | services/gameserver/src/models/team.py |
| Team member roles | services/gameserver/src/models/team_member.py |
| Team API | services/gameserver/src/api/routes/teams.py |
| Team war player UI | 🚧 tip-absent — source map historically cited TeamWarPanel.tsx; git grep TeamWar under services/player-client on origin/feat 46bce720 is empty. Do not treat as player-live. |
| Fleet service | services/gameserver/src/services/fleet_service.py |
| Crew / team console | services/player-client/src/components/teams/TeamManager.tsx |
| Faction standings MFD | services/player-client/src/components/mfd/pages/ReputationPage.tsx |
| Name-color HUD | services/player-client/src/components/layouts/StatusBar.tsx (name_color) |
Player-facing affordances¶
Verified vs Sectorwars2102 origin/feat 46bce720. Canon: you do not join an NPC faction.
- ✅ Team create / invite / kick / treasury / chat —
TeamManager.tsxCREW MANIFEST (teamAPI.createTeam,inviteMember, treasury deposit/withdraw/transfer).TeamSummaryTab.tsxis a compact layout surface. - ✅ Faction standings (not join) —
ReputationPage.tsxlists per-factionfactionAPI.getReputation()+ catalog merge; live-patch onreputation_changed/team_reputation_changedWS. No “join faction” control. - ✅ Personal name color —
StatusBar.tsxpaintsplayerState.name_color(ranking / Wanted chroma, not a faction-join UI). - 🚧 Team-war panel — GS
team_war_service.pymay exist; player-clientTeamWarPanel.tsxis not on tip (git grepTeamWarempty). Keep 🚧 until a land SHA is a tip ancestor. - 🚧 Faction consumption of team standing — ✅ pricing via
get_trade_modifiertip-shipped (46bce720/ #752); ✅ port-access viacheck_reputation_gatetip-shipped (46bce720/ #753 / LEG-814); 🚧 mission gates from team-rep still unbuilt (this page § Team reputation Status / Soft-HOLD #1305). - 📐 Galactic Concord / The Cabal — Design-only on this page’s faction table; do not ✅ a Concord/Cabal join or dossier UI.
- 📐 Multi-team alliances — § Alliances remains design-only.
Quick comparison¶
| Faction | Team | |
|---|---|---|
| Members | NPC organization | Up to 4 player accounts |
| Joining | Implicit (you have standing with all of them) | Explicit invite/create |
| Multiple | Yes — standing with all simultaneously | One team per player |
| Resource sharing | No | Treasury (12 types) |
| Combat coordination | No | Yes (fleets, formations, vision sharing) |
| Reputation effect | Per-faction pricing + port access + patrol response | Aggregate team-wide diplomatic standing |
| Cost | Free | 10,000 credits to create |
| Persistence | Permanent | Disbandable; 24h re-join cooldown |