---
id: world-map-and-portals/001
title: Spec the world graph — areas, portals, per-area rules, and the public/private split
epic: world-map-and-portals
state: done
priority: 1
blocked_by: []
estimate: L
created: 2026-08-12
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/docs/design/ClaudeDesignWorldMapBrief/HANDOFF World Map 1 - Data, Portals, Classes.md
---

## What

Write the specification for the world at the scale of areas — the layer above ground and
below "the game". Output is a design document in `land-of-lor/docs/` (agent-authored, so
it goes in the shared part of the tree, not the human-owned `docs/thought-specs/`), not
code. Kris's intent is that this spec then feeds a full implementation plan.

Read `epics/world-map-and-portals/overview.md` first — it carries the source material
from Kris's 2026-08-12 voice brief and, importantly, the list of primitives that already
exist in `land-of-lor/game/director/` so this doesn't get re-invented.

## What the spec must settle

1. **The name.** Kris is calling them *rooms* provisionally and explicitly asked what they
   should be called. Areas? Realms? Rooms? Pick one, argue it, use it consistently
   everywhere afterwards.
2. **What an area is as a designed object** — beyond "a `worlds` row". Name, description,
   who owns it, whether it's public or private, and its rule set.
3. **Per-area rules as a first-class concept.** Kris's example: a battle arena won't let
   you bring your whole inventory in. Define what dimensions a rule set can constrain
   (inventory, resources, PvP, building, persistence) and how an area declares them.
   This is the concept that later makes offline play a rule set rather than a fork.
4. **Portals as authored objects.** They exist in the protocol today
   (`PortalConfigSchema`, `SetWorldPortalsRequest`, `portal_transfer_*`) as test-scoped
   configuration applied at next spawn. Specify how a portal becomes a designed, named,
   durable part of the world instead — including whether portals are one-way or paired,
   and what a player sees and does at one.
5. **The public/private split.** The public commons and its onward areas (village of Lor →
   beginner battle arena → the Badlands, per Kris's sketch) versus each player's home area
   and portals to friends' realms. `world_members` is already the whitelist mechanism.
   Settle what a player can *see* of a realm they can't enter.
6. **Servers.** There will be more than one. Define what a server is to a player, what is
   and isn't shared across servers (accounts? collections? friends?), and therefore what
   the site's server dropdown is actually selecting.
7. **Authored vs. derived.** State plainly whether the canonical map is hand-authored
   content or a live projection of `worlds` + portal rows. Both surfaces in task 002
   depend on this answer, so it cannot be left open.
8. **Offline play — as a constraint, not a feature.** Do not design offline play here.
   Do state, for each decision above, whether it forecloses offline play, and reject any
   design that does. Kris's position, verbatim in spirit: probably not possible in the
   current build, wanted in the final version, and announced now precisely so nothing is
   built that rules it out.

## Definition of done

A document that answers 1–8 with decisions (not option lists), each with a short reason,
and that an implementation plan can be written directly from. Where a decision genuinely
belongs to Kris, state a recommendation and mark it — don't leave a bare question.

## Input Kris still owes

**The paper sketch of the Lor map.** Kris drew it on 2026-08-12; it is not in the repo.
The initial public topology should come from that sketch, not from invention. If it still
isn't available when this task is picked up, spec everything else and leave the concrete
topology as a clearly-marked placeholder rather than guessing at Kris's world.

## Assumptions

- The output belongs in `land-of-lor/docs/` as agent-authored design material. If Kris
  wants it in the workspace instead, it moves — the location isn't load-bearing.
- Spec-only, no code, matching how `world-traversal-and-ground/001` was scoped.


---

## Answered, 2026-08-15

The spec exists, in two pieces Kris authored and commissioned rather than an agent
inventing: `land-of-lor/docs/specs/CORE_ENTITIES.md` (Kris, 2026-08-15) and the three
Claude Design handoffs in `land-of-lor/docs/design/ClaudeDesignWorldMapBrief/`. Between
them the eight questions are settled, and the implementation has landed against them
(tasks 003–007). The decisions, for the record:

1. **The name is Area.** Not "room", not "realm" — used consistently in the DB model, the
   loader, both map surfaces and the editor. The `worlds` table keeps its name because
   "worldId" is the wire protocol, the GDScript and every CLI harness; a table-only rename
   would split the vocabulary in two. `land-of-lor/game/director/src/areas.ts` is where the
   new vocabulary lives.
2. **An Area as a designed object:** slug, name, type (`shared` / `home` / `battle_arena`,
   from a lookup table so a new type is one insert), access (`public` / `restricted` /
   `private`), owner, grid size, map position, and an optional parent for a nested room.
3. **Per-area rules are first-class** — a row per rule in `area_rules` with a JSON `params`
   column, never a text blob, so giving a rule parameters later isn't a migration of meaning.
4. **Portals are authored objects** in their own table. One row is one portal even when it
   goes both ways; the return leg occupies the destination cell. One-way is a flag, not a
   second row. `to_special = 'home_area'` resolves per player at travel time.
5. **Public/private:** public areas auto-member whoever walks in (this was the change that
   made authored travel possible at all); private and restricted still gate on
   `world_members`. A realm you can't enter is **fogged, not hidden** — the map returns its
   position and nothing else, because hiding a ball its neighbour can see makes the graph lie.
6. **A server** is a `servers` row; there is one, **Spellgrove**, and the picker reads it
   from data rather than hardcoding it.
7. **Authored, not derived.** The canonical world is
   `land-of-lor/game/content/spellgrove.world.json`, edited by the local editor, reviewed in
   git, applied to a database by `npm run load-world`. Both map surfaces then read the
   database through one public endpoint.
8. **Offline is not foreclosed.** Nothing above requires the network to *describe* the
   world: the authored world is a file, area rules are data, and the map surfaces are
   readers. What is not offline-ready remains what always was — the live shared areas.

**Dropped:** `Portal.distance` and `Portal.direction`. They existed only to hint map
layout, and the editor now authors positions directly (`Area.map_x` / `map_y`). The one
survivor of that family is nesting (`parent_area_id`), because "the Market Stalls are a room
off the Village" says something true about the world rather than about drawing.

**No longer blocked on the paper sketch.** Kris's 2026-08-12 sketch was never photographed
into the repo; the design's nine-area world supersedes it as the starting topology.