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:
- No Realm Power number. Nothing rolled territory, buildings, army, and prestige into a single speakable measure of a realm's standing.
- 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).
- 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:
- Reads the current active
PLAYER_BASEkingdoms and checks which of the six named factions () already have an AI-owned kingdom on the board (owner id prefixedAI_FACTION_). - 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 — aPLAYER_BASE-classified, protected starting kingdom named exactly the faction's canonical name. - 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
AIPersonalityarchetype — no new wiring needed (factions.md § Wiring already documents this path). - 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_testsseed (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 ledgeroutside 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 whenrealmPowerLastWeekis 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.
Related docs
- 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.