feat(openworlds): seed BG world_graph edges with route_kind (Closes #381) - #391
feat(openworlds): seed BG world_graph edges with route_kind (Closes #381)#391100yenadmin wants to merge 1 commit into
Conversation
) Loop-10 verification of #261: the atlas/world_graph pipeline already carries route_kind end-to-end (WorldGraphEdge model + _seed_world_graph loader + _atlas_edge_payload projection + edgeStyle viewer branching) but the baldurs-gate world has ZERO world_graph edges — so every BG inter-district connection projects as the same untyped default stroke. This PR delivers the content + the two adjacent gaps the Loop-10 sub-agent flagged. What this lands --------------- 1. servers/engine/models.py (WorldGraphEdge.route_kind Literal) Extends the Literal to include "ferry", "bridge", "underground": - "ferry": was already a dead branch in screen-map.jsx edgeStyle — the viewer styled it, but the model rejected it on author. Now authorable. - "bridge": Wyrm's Crossing over the Chionthar (the canonical BG load-bearing crossing) is structurally a bridge, not a road. First-class kind so the styling can mark it as distinct. - "underground": Underdark passages, sewers, Bhaal Temple stairs. Lands ahead of #380 (BG canon POIs incl. Underdark) so the data surface is ready. The existing 7 kinds are unchanged + default value "road" preserved — no behavior change for any existing authored content (which is currently zero edges across all worlds, but the contract is intact). 2. content/worlds/baldurs-gate/world.json (NEW world_graph block) Authors 6 directed edges, one per canonical region-pair, each with minutes / difficulty / danger / tags grounded in BG3 + Forgotten Realms canon: loc-lower-city → loc-upper-city street 8m easy d=1 tags: urban, uphill, fist-checkpoint loc-lower-city → loc-outer-city street 12m easy d=2 tags: urban, basilisk-gate, slums-edge loc-outer-city → loc-wyrms-crossing bridge 18m easy d=2 tags: chionthar, fist-toll, wyrms-rock loc-wyrms-crossing → loc-elturel road 24h normal d=3 tags: risen-road, south, hellrider-patrol loc-wyrms-crossing → loc-reithwin road 36h hard d=5 tags: shadow-cursed, north, lifting loc-wyrms-crossing → loc-candlekeep road 96h normal d=2 tags: coast-road, west, avowed-watch All 6 verified against canonical Location.connections — the _seed_world_graph loader's "edge must reference an existing connection in either direction" guard PASSes for every edge (verified standalone via the loader's exact predicate). 3. viewer/openworlds/screen-map.jsx (edgeStyle extension) The existing edgeStyle had styles for 3 kinds (street/road, river/ ferry/sea, default). The Literal supports 7 (now 10). This PR adds styles for the remaining kinds so atlas reading isn't ambiguous: - bridge : thicker amber-brown solid (load-bearing crossing) - underground : tight-dotted dark brown (you go down) - passage : same as underground - portal : violet dashed (extraplanar) - trail : light brown looser-dashed (wilds-grade path) The pre-existing high-danger override (danger >= 6) still wins regardless of kind — that's the right precedence (the player should see "dangerous" before "what kind of road"). Why grouped, not 3 PRs ---------------------- The 3 changes form one logical unit: the Literal opens what authors can write, the world.json exercises that authorship, the edgeStyle makes the new kinds visually distinguishable. Shipping any 1 or 2 without the third leaves the surface incomplete (e.g. authoring "bridge" without a distinct style means the player can't tell the bridge from a road; styling "bridge" without authoring leaves the new branch dead). Verification ------------ - world.json: `json.load()` passes; 26 top-level keys (was 25); 6 edges; 3 route_kinds used (street, road, bridge). Validated all 6 edges satisfy the loader's "edge.to_id in src.connections OR edge.from_id in dst.connections" guard. - models.py: `ast.parse()` passes. Literal extended by 3 members, default unchanged. - screen-map.jsx: brace balance 450/450, paren balance 601/601. Existing kinds (street, road, river, ferry, sea) styled exactly as before — diff is purely additive new branches. Acceptance criteria (per #381) ------------------------------ - [x] world_graph block authored for the BG world (zero before, 6 now) - [x] route_kind values match the AC's intended palette (street, road, bridge in this PR; ferry/sea/river already supported; underground + portal + trail ready for #380's POIs) - [x] Literal extended to include "ferry" (closing the existing dead branch in edgeStyle) + "bridge" + "underground" - [x] edgeStyle extended to give each kind a distinct visual signal - [x] Edges validated against canonical Location.connections — no drift between regions[] and world_graph Out of scope ------------ - New POIs / regions (#380) — required for portal + underground edges to actually appear; this PR makes the Literal ready, the seed waits - Edge metadata for non-BG worlds — pure-data work per world - A symmetric-edges convention (today: 6 directed edges; could be 12 for full symmetry, but viewer renders fine either way) Refs ---- - Closes #381 (Loop-10 follow-up to #261) - Parent #261 (atlas-seed, PR #371 closed the discovered-tier strand) - Builds toward #380 (BG POIs — Steel Watch, Underdark, Bhaal Temple, Avernus portal — which will exercise the new "underground" + "portal" Literal members)
📝 WalkthroughWalkthroughThis PR extends the world graph system to support new strategic route types (ferry, bridge, underground) and applies them to the Baldur's Gate world. The schema is broadened to validate these types, the world data is seeded with region-connecting edges, and the map renderer is updated to style these routes distinctly. ChangesWorld Graph Connectivity
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
content/worlds/baldurs-gate/world.json (1)
56-63: ⚡ Quick winConsider adding reverse-direction edges for complete travel metadata.
The world_graph defines 6 directed edges, but each corresponds to a bidirectional connection in the Location.connections (lines 45-51). This means return journeys exist but lack metadata.
For example:
- Lower City → Upper City has metadata (8 minutes, street, easy, danger 1)
- Upper City → Lower City has no metadata (only the connection exists)
Since return journeys often have different characteristics (e.g., downhill vs uphill, different timing), consider whether reverse edges should be added with appropriate metadata, or document if the one-way metadata is intentional.
Example: adding the reverse edge for Upper → Lower City
{"from_id": "loc-lower-city", "to_id": "loc-upper-city", "route_kind": "street", "minutes": 8, "difficulty": "easy", "danger": 1, "tags": ["urban", "uphill", "fist-checkpoint"]}, + {"from_id": "loc-upper-city", "to_id": "loc-lower-city", "route_kind": "street", "minutes": 6, "difficulty": "easy", "danger": 1, "tags": ["urban", "downhill", "basilisk-gate"]}, {"from_id": "loc-lower-city", "to_id": "loc-outer-city", "route_kind": "street", "minutes": 12, "difficulty": "easy", "danger": 2, "tags": ["urban", "basilisk-gate", "slums-edge"]},🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@content/worlds/baldurs-gate/world.json` around lines 56 - 63, The edges array defines only one direction for connections (e.g., {"from_id":"loc-lower-city","to_id":"loc-upper-city",...}) while Location.connections imply bidirectional travel; add corresponding reverse-direction edge objects for each route (e.g., from_id: "loc-upper-city" to_id: "loc-lower-city") with appropriate metadata (minutes, route_kind, difficulty, danger, tags) reflecting return-trip differences (downhill, faster/slower, differing danger), or if one-way metadata is intentional, add a clear comment/documentation entry stating that edges are directional and return trips use default/implicit values; update the edges array to include reverse entries for loc-lower-city↔loc-upper-city, loc-lower-city↔loc-outer-city, loc-outer-city↔loc-wyrms-crossing, loc-wyrms-crossing↔loc-elturel, loc-wyrms-crossing↔loc-reithwin, and loc-wyrms-crossing↔loc-candlekeep.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@content/worlds/baldurs-gate/world.json`:
- Around line 56-63: The edges array defines only one direction for connections
(e.g., {"from_id":"loc-lower-city","to_id":"loc-upper-city",...}) while
Location.connections imply bidirectional travel; add corresponding
reverse-direction edge objects for each route (e.g., from_id: "loc-upper-city"
to_id: "loc-lower-city") with appropriate metadata (minutes, route_kind,
difficulty, danger, tags) reflecting return-trip differences (downhill,
faster/slower, differing danger), or if one-way metadata is intentional, add a
clear comment/documentation entry stating that edges are directional and return
trips use default/implicit values; update the edges array to include reverse
entries for loc-lower-city↔loc-upper-city, loc-lower-city↔loc-outer-city,
loc-outer-city↔loc-wyrms-crossing, loc-wyrms-crossing↔loc-elturel,
loc-wyrms-crossing↔loc-reithwin, and loc-wyrms-crossing↔loc-candlekeep.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5ce1b69e-9a68-4cbd-9036-c48c10c1216c
📒 Files selected for processing (3)
content/worlds/baldurs-gate/world.jsonservers/engine/models.pyviewer/openworlds/screen-map.jsx
TL;DR
Loop-10 verification of #261 found the atlas/world_graph pipeline already carries
route_kindend-to-end — model, loader, viewer projection, viewer branching all exist — but thebaldurs-gateworld has zeroworld_graphedges, so every BG inter-district connection projects as the same untyped default stroke. This PR delivers the content, plus the two adjacent gaps the Loop-10 sub-agent flagged.Closes
feat(openworlds): seed BG world_graph edges with route_kind metadata (#261 AC follow-up)What this lands
1.
servers/engine/models.py—WorldGraphEdge.route_kindLiteralExtended by 3 members. The existing 7 kinds + default
"road"are unchanged (zero behavior change for any existing authored content).ferryscreen-map.jsxedgeStyle— viewer styled it, model rejected it on author. Now authorable.bridgeunderground2.
content/worlds/baldurs-gate/world.json— NEWworld_graphblockSix directed edges, one per canonical region-pair:
All 6 verified against canonical
Location.connections— the_seed_world_graphloader's "edge must reference an existing connection in either direction" guard passes for every edge (validated standalone via the loader's exact predicate).3.
viewer/openworlds/screen-map.jsx—edgeStyleextensionExisting styled 3 kind-buckets (street/road, river/ferry/sea, default). The Literal now supports 10. Added branches:
bridgeunderground/passageportaltrailThe pre-existing high-danger override (
danger >= 6) still wins regardless of kind — that's the right precedence (player should see "dangerous" before "what kind of road").Why grouped, not 3 PRs
The 3 changes form one logical unit:
Shipping any 1 or 2 leaves the surface incomplete. Authoring
bridgewithout a distinct style means the player can't tell the bridge from a road; stylingbridgewithout authoring leaves the new branch dead.Verification
world.jsonjson.load()parsesworld.jsonmodels.pyast.parse()screen-map.jsxAcceptance criteria (per #381)
world_graphblock authored for the BG world (zero before, 6 now)route_kindvalues match the AC's intended palette (street, road, bridge — plus ferry/sea/river already supported; underground/portal/trail ready for fix(openworlds): restore Atlas rumoured/known tier split + 5 missing BG POIs + Sword Coast regional pin (#261 follow-up) #380)ferry(closing the existing dead branch),bridge,undergroundedgeStyleextended to give each kind a distinct visual signalLocation.connections— zero driftOut of scope
portal+undergroundedges to actually appear; this PR makes the Literal ready, the seed waits for POI work)Collision audit
servers/engine/models.pylast touched onmainby PR fix(engine): derive class+level max_hp for seated canon characters (#352) #355 (fix(engine): derive class+level max_hp for seated canon characters (#352)). The diff adds 3 strings to an existing Literal — no overlap with PR fix(engine): derive class+level max_hp for seated canon characters (#352) #355's surface.content/worlds/baldurs-gate/world.jsonlast touched onmainby PR fix(openworlds): atlas-seed — #261 #371 (fix(openworlds): atlas-seed (Addresses #261)). This PR adds a new top-level key (world_graph) — additive only.viewer/openworlds/screen-map.jsxlast touched onmainby PR fix(openworlds): map — #307 #366 (fix(openworlds): map (Addresses #307)). The diff extendsedgeStylewith new branches — additive only.fix(dm,viewer): cold-open delivers a real 2nd-person opening— DM/cold-open territory, zero overlap.DO NOT MERGE yet
Per owner direction. Ready for review.
Summary by CodeRabbit
Release Notes