Branching zones G9a: remove GOGOBEE_BRANCHING_ZONES gate

All 9 zones have hand-authored graphs (G8a–G8i), so the POC gate has
served its purpose. Make graph mode the only runtime path:

- Drop branchingZonesEnabled() and the os import in zone_graph_nav.go.
- Inline the gate-on branches in zoneCmdMap, zoneCmdAdvance,
  CurrentRoomType, startZoneRun, and the expedition map renderer.
- Keep the legacy linear functions (markRoomCleared, renderZoneMap,
  generateRoomSequence) standing — their tests still exercise them and
  they'll retire alongside current_room / room_seq_json in G9b.
- Strip gate test (TestBranchingZonesGate, TestCurrentRoomType_GateOff)
  and t.Setenv calls from the surviving graph tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-09 17:17:19 -07:00
parent 304ad27c0f
commit 103cf30987
8 changed files with 72 additions and 151 deletions

View File

@@ -85,21 +85,17 @@ func (r *DungeonRun) IsActive() bool {
}
// CurrentRoomType returns the type of the room the player is currently
// standing in. In gated graph mode (GOGOBEE_BRANCHING_ZONES=1) with a
// hand-authored ZoneGraph registered for the run's zone, the live
// CurrentNode's kind is authoritative — that's the only way side paths
// (e.g. the Crypt of Valdris secret chamber) resolve to the right
// room-type when the player diverges from the canonical RoomSeq. The
// legacy RoomSeq fallback covers gate-off runs and runs in zones whose
// graph is still the linear-compiled one. Returns "" if no resolution
// is possible.
// standing in. The live CurrentNode's kind is authoritative — that's
// the only way side paths (e.g. the Crypt of Valdris secret chamber)
// resolve to the right room-type when the player diverges from the
// canonical RoomSeq. The legacy RoomSeq fallback covers in-flight runs
// from before the G4 dual-write deploy that lack a CurrentNode entry.
// Returns "" if no resolution is possible.
func (r *DungeonRun) CurrentRoomType() RoomType {
if branchingZonesEnabled() && r.CurrentNode != "" {
if _, authored := zoneGraphRegistry[r.ZoneID]; authored {
if g, ok := loadZoneGraph(r.ZoneID); ok {
if n, exists := g.Nodes[r.CurrentNode]; exists {
return nodeKindToRoomType(n.Kind)
}
if r.CurrentNode != "" {
if g, ok := loadZoneGraph(r.ZoneID); ok {
if n, exists := g.Nodes[r.CurrentNode]; exists {
return nodeKindToRoomType(n.Kind)
}
}
}
@@ -228,15 +224,12 @@ func startZoneRun(userID id.UserID, zoneID ZoneID, dndLevel int, rng *rand.Rand)
}
// G4 dual-write: persist the entry node id and seed visited_nodes
// with it, so navigation surfaces in G5 can read graph state without
// further migration. G7: when graph mode is on AND the zone has a
// hand-authored graph, start at that graph's Entry node so the player
// actually traverses the authored topology rather than falling off
// into the legacy `<zone>.r1` namespace.
// further migration. New runs always start at the registered graph's
// Entry node; only zones that lack a registered graph (none, post-G8)
// would fall back to the legacy linear `<zone>.r1` namespace.
entryNode := deriveLegacyNodeID(zoneID, 0)
if branchingZonesEnabled() {
if g, ok := zoneGraphRegistry[zoneID]; ok {
entryNode = g.Entry
}
if g, ok := zoneGraphRegistry[zoneID]; ok {
entryNode = g.Entry
}
visitedJSON, _ := json.Marshal([]string{entryNode})
run.CurrentNode = entryNode