Skip to main content

The Eternal Realm (Phase 4, IMPLEMENTED 2026-07-18)

The Eternal Realm (Phase 4, IMPLEMENTED 2026-07-18)

Status: IMPLEMENTED — Realm Power rollup, the speakable Realm Ledger (rank + trend), weekly growth narration on the return briefing, and AI-faction board population all landed on the existing

Last updated: August 30, 2026

Lineage — The Eternal Realm (Phase 4, IMPLEMENTED 2026-07-18)

Status: IMPLEMENTED — Realm Power rollup, the speakable Realm Ledger (rank + trend), weekly growth narration on the return briefing, and AI-faction board population all landed on the existing Global-mode infrastructure.

Parent: vision.md § Mode 2 — The Eternal Realm (the north-star ask this doc fulfills — Phase 4 of the build path). Builds on: index.md § Global World (the persistent 1000x1000 world, action economy, root-kingdom protection), factions.md (the named-faction roster this doc populates the board with), return-briefing.md (the entry-time push surface this doc extends with growth narration), empire-4x-layer.md (the seven voice-memory rules this doc is bound by).

Why this doc exists

vision.md names Mode 2 directly: "The Global world becomes The Eternal Realm: a persistent kingdom you nurture across weeks... Today it holds 7 kingdoms and has no growth-and-compare loop — that loop is the whole point of this mode." Three things were missing before this phase:

  1. No Realm Power number. Nothing rolled territory, buildings, army, and prestige into a single speakable measure of a realm's standing.
  2. No leaderboard. A player had no way to hear their rank against other realms, the realm holding the throne of power, or whether they were rising or falling — "Clash of Clans is nothing without the leaderboard" (vision.md).
  3. No living board. only ever spawned REAL PLAYER kingdoms. Zero AI kingdoms existed in the Global world unless a real player's kingdom went abandoned for 30+ days. The named-faction roster (factions.md) was wired into the personality-assignment path but nothing ever called it to actually populate the Global board.

This doc is the design for the fix: reuse existing numbers for Realm Power (rule 3 of empire-4x-layer.md — no new currency), add ONE new pull-on-demand surface (the Realm Ledger), extend the existing return-briefing push with a one-line growth narration, and seed the named-faction roster onto the Global board so raiding has a living opponent even when the human population is thin.

The build-grow-compare loop

The Eternal Realm's loop is: build (recruit, construct, claim expansion territory — all existing Global-mode mechanics), grow (the kingdom visibly gets bigger across weekly turns — more territory, more buildings, a stronger army, rising prestige), compare (hear your rank against every other realm, on demand), repeat forever. No win state — longevity and standing are the reward (vision.md § Difficulty & win conditions across modes).

Each weekly Global turn (.processAllKingdoms):
  → existing: worker production, territory production, seasonal effects, AI turns, random events
  → NEW: ensure the named-faction roster is present on the board (self-heals weekly)
  → NEW: snapshot every active realm's current Realm Power + rank for next week's trend comparison

On demand (pull, any time):
  → "realm ledger" / "my rank" / "realm power" → speak rank, own power, the realm holding the
    throne of power, and the weekly trend

On session entry (push, once per session, Global mode only):
  → the return briefing's existing snapshot + delta gains one line: current Realm Power vs last
    week's stored snapshot, with a pointer to the Realm Ledger for the full ranking

Realm Power — a pure rollup of existing numbers

Formula (implemented in ):

Realm Power = (territory count × 10)
            + (total building count across owned territories × 5)
            + (sum of .calculateArmyStrength(army) across all armies)
            + dynasty prestige (LineageKingdom.getDynastyPrestige())

No new currency, no new tracked resource — every term already exists on the kingdom the player already sees (territory count on the resource screen, buildings on the buildings view, army strength in combat resolution, prestige on the Legacy and Empire screens). The weights (10 per territory, 5 per building) are deliberately simple integers so the number reads naturally in speech and a player who studies the formula can reason about which lever moves it most — territory and army dominate early, prestige compounds late (mirroring how Empire Tier's passive prestige income already rewards longevity).

Why these four terms and not others: they are exactly the four vision.md names — "Realm Power = territory + buildings + army + prestige, numbers the player already has." Resources (gold, food, wood, stone, iron, mana) are deliberately excluded — they are consumable/spendable, not standing measures of a realm's built-up strength, and including them would make Realm Power swing wildly with a single big purchase rather than reflecting durable growth.

The Realm Ledger — the speakable leaderboard

Command: realm ledger / my rank / realm power / leaderboard / who is winning (routes through the existing LINEAGE_VIEW matched-key mechanism — LineageViewType.REALM_LEDGER — exactly like EMPIRE, CHRONICLE, and every other pull-on-demand screen).

Voice shape (illustrative, mirrors the vision.md example exactly):

"You are the fourteenth realm of the age, with a realm power of 420. The Iron Compact holds the throne of power, with a realm power of 900. You rose two ranks this week. Shall you take command, or hear more of your realm?"

  • Rank + own power — always spoken first (the player's own standing is the urgent number).
  • The realm holding the throne of power — the #1 realm's name and power, UNLESS the player IS #1 (then: "Your realm holds the throne of power.").
  • Weekly trend — compares the player's CURRENT rank to LineageKingdom.realmRankLastWeek (the snapshot stamped by the weekly turn processor). Rose N ranks / fell N ranks / held rank. Suppressed entirely on the first week a realm exists (no snapshot yet — rule 6, suppress empty sections).
  • Not yet in the Global world — a calm redirect offering to enter the Global World, never a dead-end (rule 7): "The realm ledger ranks the eternal realms of the Global World. Enter the global world to build a realm and take your place among them."
  • Ends on a handoff — take command or end turn, same pattern as every other Lineage view.

Voice-memory compliance (empire-4x-layer.md's seven rules): pull-on-demand only (rule 1), re-readable any time (rule 2), reuses existing numbers — territory/buildings/army/prestige, no new currency (rule 3), a single number plus one comparison point, never a wall of ranks (rule 4/5), suppresses the trend line with no prior snapshot (rule 6), always ends on a handoff (rule 7).

Ranking scope and mechanics (): reads every active lineage.lineage_global_kingdom row via , keeps ONE row per unique kingdomId (preferring the PLAYER_BASE row when a kingdom has more than one active row), computes Realm Power for each, and sorts descending. This mirrors exactly how the rest of Global-mode code already treats "a realm" — one representative row via , with territories/armies denormalized into that row's kingdomData (the same convention and already follow). The Global world today has a small kingdom count (single digits to low hundreds), so an in-memory sort every call is cheap; if the world grows into the thousands, a DB-side computed-power column becomes the natural follow-up (not needed at this scale).

Weekly trend — the snapshot mechanic

LineageKingdom carries two new fields, embedded in the existing kingdom_data JSON column (no schema migration — the field lives on the same denormalized object every Global kingdom row already persists):

  • realmPowerLastWeek (Integer, nullable) — the realm's computed power as of the last weekly snapshot.
  • realmRankLastWeek (Integer, nullable) — the realm's rank as of the last weekly snapshot.

runs once per weekly Global turn, at the END of (after every kingdom's turn has processed, so the snapshot reflects the state AFTER this week's growth, ready to be "last week" for next week's comparison). It computes the full ledger, stamps each kingdom's two new fields, and persists each changed row through the existing path — the same write-back mechanism every other Global-mode mutation already uses. This is the SAME delta-snapshot pattern LineageBriefingSnapshot already uses for the return briefing's turn-to-turn delta (mirrored, not duplicated — a separate mechanism because the return briefing snapshot is per-PLAYER-session while the realm-power snapshot is per-WEEKLY-TURN and shared by every reader of the ledger).

Growth narration on the return briefing

The return briefing (return-briefing.md) already narrates a kingdom's snapshot and delta on session entry. This phase adds ONE new line, Global-mode only, gated on a snapshot already existing (first week is silently suppressed — rule 6):

"Your realm power has grown to five hundred and twelve, up forty since last week. Say realm ledger to hear where you rank among the realms."

This is deliberately terse — the FULL ranking (rank, throne-holder, trend) stays a pull-on-demand answer via the Realm Ledger command, never pushed in full on every login (rule 1: push the urgent, pull the rest). The briefing's job is to make growth VISIBLE and point at where to learn more, not to repeat the ledger's own content.

Raid and defend on expansion territory (confirmed, not rebuilt)

vision.md states this is "already true" and the code confirms it: already enforces base-protected / expansion-conquerable in the Global world (validateAttack refuses any attack on a PLAYER_BASE-classified kingdom; LineageTerritory.canBeAttacked() / LineageGlobalKingdom.canBeAttacked() both gate on territoryClassification / territoryType), and already wires attack territory to a live voice command in Global mode, complete with combat resolution, casualties, war spoils, and contest-to-conquest over four days. This phase does not touch that mechanic — it makes the raid loop mean something by giving the board living, standing opponents to raid (below), and by giving the player a reason to care about growth (the Ledger).

Long-lived AI factions populate the board

The gap: had exactly one kingdom-creation path — spawnPlayerKingdomAt, called only for a real player's FIRST entry into Global mode. Nothing ever created an AI-controlled kingdom in the Global world. already treats an abandoned real player's kingdom (30+ days inactive) as AI-controlled and runs against it every week — but with zero real players ever going abandoned, and only 7 total kingdoms, the board was a near-empty, near-static "free-for-all" with nothing to compare against and little to raid.

The fix: — a self-healing top-up, reusing 100% existing machinery:

  1. Reads the current active PLAYER_BASE kingdoms and checks which of the six named factions () already have an AI-owned kingdom on the board (owner id prefixed AI_FACTION_).
  2. For every faction NOT yet present, spawns one using the SAME spawn-position search (findNextSpawnPosition — respects the existing 7-square ring buffer so a faction kingdom can never collide with, or crowd out spacing for, a future real player) and the SAME kingdom-creation path (spawnPlayerKingdomAt) real players use — a PLAYER_BASE-classified, protected starting kingdom named exactly the faction's canonical name.
  3. Because the kingdom's name exactly matches a roster entry, the EXISTING personality-assignment path (, called the first time runs against it) deterministically assigns that faction's fixed AIPersonality archetype — no new wiring needed (factions.md § Wiring already documents this path).
  4. Because the owner id starts with AI_, is already true for it, so it is processed every weekly turn exactly like any other AI kingdom — it recruits, builds, claims expansion territory, and grows on its own, using the SAME logic that already drives Local/Multiplayer AI opponents. This is what makes the factions "long-lived": they are not a scripted one-time seed, they are a real AI kingdom that plays every week, forever, exactly like a human's would.

Where it runs: ensureFactionKingdoms() is called from two points, both cheap (the check is a single indexed query plus an early-exit once the roster is present):

  • , once at the START of every weekly turn — this is the unconditional self-heal that backfills the roster even for a Global world that already has players and never triggers a fresh spawn (closing the gap for the CURRENT prod state, which has 7 real kingdoms and zero AI ones today).
  • , when a brand-new player kingdom is spawned — so the very first player to enter the Global World after this phase ships sees a populated board immediately, not after next week's turn.

Because the AI kingdoms are PLAYER_BASE-classified (protected, matching vision.md's "base stays protected — already true"), their OWN base cannot be raided — but as they grow, their AI turn processing claims EXPANSION territory around their base exactly like a real player would, and THAT expansion territory is raidable under the exact same rules a real player's expansion territory is. This is what gives the free-for-all living stakes without inventing a second protection model.

Voice-memory compliance summary (empire-4x-layer.md's seven rules)

Rule How the Eternal Realm loop obeys it
1. Push urgent, pull rest Only the one-line growth delta is pushed (return briefing); full ranking is pull-on-demand (Realm Ledger)
2. Re-readable on request realm ledger / my rank / realm power all route to the same live-computed answer, any time
3. Reuse existing numbers Realm Power is a rollup of territory, buildings, army strength, and prestige — zero new currencies
4. Cap concurrent decisions at three The Ledger is a READOUT, not a decision surface — it presents information, then the standard two-option handoff (take command / end turn)
5. Adaptive length / progressive disclosure The Ledger speaks rank + power + throne-holder + trend, four short facts, never a full table of every realm
6. Suppress empty sections No prior snapshot → no trend line; empty ledger / not-yet-in-Global-world → a calm redirect, not a blank
7. Actionable handoff Both the Ledger and the briefing's growth line end pointed at a concrete next command

Regression shape (hermetic)

Per .claude/rules/hermetic-scenario-tests.md:

  • Realm Power formula — pure function over kingdom/territories/armies; JUnit-tested directly () plus a hermetic game_test.scenario_tests seed (known territory/building/army/prestige state, realm power → asserts the spoken number matches the formula).
  • Realm Ledger rank + trend — seed a Global-mode kingdom with a known realmRankLastWeek, assert the rose/fell/held phrasing; seed with no prior snapshot, assert the trend line is silently suppressed. realm ledger outside Global mode asserts the redirect, never a dead-end.
  • Growth narration — seeded on the launch-briefing scenario chain (global world; <name>; yes; ...), assert the growth line appears only when realmPowerLastWeek is present.
  • AI-faction seeding — (JUnit): ensureFactionKingdoms() on an empty board creates exactly six kingdoms named for the six roster factions; called again is a no-op (idempotent); never places a faction kingdom inside another kingdom's buffer zone.

Out of scope / deferred

  • War of the Realms scenario sides (Mode 3) — the faction roster is already exposed as mode-agnostic data (factions.md); this phase does not build the scenario framing.
  • DB-side computed Realm Power column — the in-memory sort over getAllActive() is adequate at today's Global-world scale (see § Realm Ledger); revisit only if the kingdom count grows into the thousands.
  • "Who raided me" notification surface — raid/defend already works end to end (combat resolves, casualties apply, contest-to-conquest runs); a dedicated raid-log/notification pull surface is a future polish item, not required for the build-grow-compare loop itself.
  • vision.md — the north-star vision this doc's Phase 4 fulfills.
  • factions.md — the named-faction roster this doc populates the Global board with.
  • return-briefing.md — the entry-time push surface this doc extends.
  • empire-4x-layer.md — the seven voice-memory rules this doc is bound by.
  • index.md — current mechanics, including Global World + Root Kingdom Protection.
  • .claude/rules/hermetic-scenario-tests.md — the regression discipline for the deterministic surfaces above.