Branching zones G9c: drop legacy columns + add purge gate

Final G9 step. The schema no longer creates current_room or
room_seq_json on fresh installs, and existing prod schemas drop them
via purgeLegacyZoneRunColumns() once the operator flips
GOGOBEE_BRANCHING_PURGE=1.

- CREATE TABLE dnd_zone_run loses both columns; comment updated to
  point at G1's columnMigrations for the graph-mode columns.
- cleanup_g9.go mirrors the L5 idempotent-purge shape: ALTER TABLE
  DROP COLUMN per legacy column, "no such column" treated as already
  done, default-off.
- Wired into adventure Init alongside purgeLegacyAdvCharacterTable.
- Plan doc marks G9 complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-09 17:23:08 -07:00
parent a5c0a83e2a
commit 7ec78f3cd9
4 changed files with 50 additions and 16 deletions

View File

@@ -1730,16 +1730,16 @@ CREATE TABLE IF NOT EXISTS adventure_characters_pre_dnd (
-- A single in-progress or completed dungeon run. One row per run; players
-- may have at most one row with completed_at IS NULL AND abandoned = 0
-- (enforced in code, not via constraint, to keep migrations simple).
-- room_seq_json is a JSON array of RoomType strings generated at run start;
-- current_room indexes into that array. rooms_cleared is a JSON array of
-- room indices the player has resolved (combat won / trap survived / etc.).
-- rooms_cleared is a JSON array of room indices the player has resolved
-- (combat won / trap survived / etc.). The zone-graph columns
-- (current_node, visited_nodes, node_choices) are added in Phase G1's
-- columnMigrations; the legacy linear current_room / room_seq_json
-- columns retired in G9.
CREATE TABLE IF NOT EXISTS dnd_zone_run (
run_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
zone_id TEXT NOT NULL,
current_room INTEGER NOT NULL DEFAULT 0,
total_rooms INTEGER NOT NULL,
room_seq_json TEXT NOT NULL DEFAULT '[]',
rooms_cleared TEXT NOT NULL DEFAULT '[]',
boss_defeated INTEGER NOT NULL DEFAULT 0,
abandoned INTEGER NOT NULL DEFAULT 0,

View File

@@ -200,6 +200,10 @@ func (p *AdventurePlugin) Init() error {
// Phase L5 close-out — drop the now-cold adventure_characters table when
// the operator sets GOGOBEE_LEGACY_PURGE=1. Default off; idempotent.
purgeLegacyAdvCharacterTable()
// Phase G9 close-out — drop the now-cold current_room / room_seq_json
// columns from dnd_zone_run when the operator sets
// GOGOBEE_BRANCHING_PURGE=1. Default off; idempotent.
purgeLegacyZoneRunColumns()
// Phase R1 orphan-archive used to run here on every Init, but it
// over-archived: it treats any active dnd_zone_run row not linked to
// an active expedition as a legacy `!adventure dungeon` orphan, which

View File

@@ -0,0 +1,37 @@
package plugin
import (
"log/slog"
"os"
"strings"
"gogobee/internal/db"
)
// purgeLegacyZoneRunColumns drops the dnd_zone_run.current_room and
// dnd_zone_run.room_seq_json columns when the operator opts in via
// GOGOBEE_BRANCHING_PURGE=1. Phase G9 close-out: G9a removed the
// runtime gate, G9b stopped persisting the columns, and this is the
// final step.
//
// Idempotent: skips silently when the columns are already gone (or
// never existed on a fresh install — the schema in db.go no longer
// creates them).
func purgeLegacyZoneRunColumns() {
if os.Getenv("GOGOBEE_BRANCHING_PURGE") != "1" {
return
}
d := db.Get()
for _, col := range []string{"current_room", "room_seq_json"} {
if _, err := d.Exec("ALTER TABLE dnd_zone_run DROP COLUMN " + col); err != nil {
msg := err.Error()
// "no such column" → already dropped (or fresh install).
if strings.Contains(msg, "no such column") {
continue
}
slog.Error("G9 purge: drop column failed", "col", col, "err", err)
return
}
slog.Warn("G9 purge: dnd_zone_run column dropped (GOGOBEE_BRANCHING_PURGE=1)", "col", col)
}
}