Drop the eight legacy columns SP8 left behind
Migrations 66 to 73 drop `actions.index`, `variants`, `variant_index`, `variant_count`, `state_before`, and `world_state_before`, plus `adventures.memory_cursor` and `summary_cursor`. `index` is a keyword in SQLite, so migration 71 quotes it. Nothing outside the migrations read these. `models.py`, `schemas.py`, and `ACTION_LIST_COLUMNS` lose the same eight fields, `Adventure.actions` orders by `id`, and `attempts.renumber`, `context.history.max_action_index`, and `nodes.next_index` are deleted. Two changes keep the migration replayable on a `create_all` database: - `_split_variants_into_siblings` wrote through the live ORM table, so it stopped compiling once migration 66 removed five of its columns. It now writes through `_ACTIONS_AT_60`, a frozen `Table` with its own `MetaData`. - Five data passes read columns these migrations drop. Each now calls `_has_columns` and returns early when the columns are absent. `bootstrap` takes a `through` version so a migration test can stop at the schema it asserts on. 555 tests pass, up from 549. Eight of the new cases assert each column is gone after a real schema-45 database migrates all the way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0198qDK3gmgSo7EtQ4GTPqqK
This commit is contained in:
committed by
Parth
co-authored by
Claude Opus 5
parent
e0bf2b61d9
commit
f1bebe18d0
@@ -67,7 +67,7 @@ def client(monkeypatch):
|
||||
)
|
||||
setup.add(adv)
|
||||
setup.flush()
|
||||
setup.add(models.Action(adventure_id=adv.id, index=0, type="start", text=OPENING))
|
||||
setup.add(models.Action(adventure_id=adv.id, type="start", text=OPENING))
|
||||
setup.add(models.AdventureScript(
|
||||
adventure_id=adv.id, position=0, enabled=True, name="Gold", output_js=GOLD_SCRIPT,
|
||||
))
|
||||
@@ -163,7 +163,7 @@ def _rows(adv_id) -> list[models.Action]:
|
||||
db.query(models.Action)
|
||||
.filter(models.Action.adventure_id == adv_id)
|
||||
.order_by(models.Action.branch_id, models.Action.depth,
|
||||
models.Action.variant_index)
|
||||
models.Action.id)
|
||||
.all()
|
||||
)
|
||||
finally:
|
||||
@@ -341,29 +341,20 @@ def test_the_lineage_is_rebuilt_rather_than_carried(client):
|
||||
assert lineage.entries_of(forked) == [(forked.id, None), (root.id, forked.fork_depth)]
|
||||
|
||||
|
||||
def test_the_legacy_index_is_reissued_so_two_branches_never_share_one(client):
|
||||
"""`index` describes the adventure. `depth` describes a path.
|
||||
def test_two_branches_each_keep_their_own_node_at_one_depth(client):
|
||||
"""A depth describes a path, not the adventure.
|
||||
|
||||
Two branches can each have a node at depth 3, so depth cannot be the
|
||||
number `max_action_index` hands out next. The import allocates one
|
||||
index per turn instead. Siblings share an index, the way SP4 leaves
|
||||
them, and no two coordinates share one.
|
||||
The fork and the branch it left both hold a turn at depth 2, and they are
|
||||
not the same turn. The import used to issue a global `index` per turn as
|
||||
well, so that every coordinate had a number nothing else held. SP8 dropped
|
||||
that column, so the coordinate is all there is, and it has to survive the
|
||||
round trip on its own.
|
||||
"""
|
||||
copy = _imported(client, _export(client, _forked_story(client)))
|
||||
rows = _rows(copy)
|
||||
by_index: dict[int, set] = {}
|
||||
for row in rows:
|
||||
by_index.setdefault(row.index, set()).add((row.branch_id, row.depth))
|
||||
assert all(len(coords) == 1 for coords in by_index.values()), \
|
||||
"one index per turn, whatever branch it is on"
|
||||
assert len(by_index) == len({(r.branch_id, r.depth) for r in rows})
|
||||
|
||||
# This is the case that makes the rule necessary: the fork and the
|
||||
# branch it left both hold a turn at depth 2, and they are not the
|
||||
# same turn.
|
||||
at_depth_2 = [r for r in rows if r.depth == 2]
|
||||
assert len({r.branch_id for r in at_depth_2}) == 2
|
||||
assert len({r.index for r in at_depth_2}) == 2, "same depth, different turns"
|
||||
|
||||
|
||||
# ------------------------------------------------------- a file that is wrong
|
||||
@@ -487,7 +478,6 @@ def test_a_v1_bundle_with_a_cursor_lands_it_on_a_node(client):
|
||||
db = SessionLocal()
|
||||
try:
|
||||
adventure = db.get(models.Adventure, copy)
|
||||
assert adventure.memory_cursor == 2, "the legacy count is kept as given"
|
||||
assert adventure.memory_cursor_branch_id is not None
|
||||
assert adventure.memory_cursor_depth == 1, "the second story action"
|
||||
finally:
|
||||
@@ -495,8 +485,7 @@ def test_a_v1_bundle_with_a_cursor_lands_it_on_a_node(client):
|
||||
|
||||
|
||||
def test_a_v2_bundle_brings_its_anchors_back(client):
|
||||
"""The other direction: v2 carries the anchor directly, and the legacy
|
||||
count is derived from it."""
|
||||
"""The other direction: v2 carries the anchor directly."""
|
||||
original = _forked_story(client)
|
||||
tip = [a for a in _rows(original) if a.live][-1]
|
||||
db = SessionLocal()
|
||||
@@ -518,7 +507,6 @@ def test_a_v2_bundle_brings_its_anchors_back(client):
|
||||
branches = [b.id for b in _branch_rows(copy)]
|
||||
assert branches.index(adventure.memory_cursor_branch_id) == 1
|
||||
assert adventure.memory_cursor_depth == tip.depth
|
||||
assert adventure.memory_cursor > 0, "the legacy count was read back off it"
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user