Answer the review, and keep the opening node's bank
Nine findings from a review of the phase-14 stack. The one about a retry withdrawing a memory is not a bug — a memory anchored to a node describes that node, and it goes when the node goes. The root is the exception, and it is the only one: migration 62 parked every memory written before memories had coordinates on depth 0, so withdrawing the opening node would retire a whole bank nobody attached there. A memory with no source range covers no story and now stays; a summary that genuinely ends there is still withdrawn. The rest are repairs. * The adventure list quoted whichever attempt was written last rather than the one the story tells, so switching back left the index disagreeing with the page. * A v1 import gave a typed memory no depth, rebuilding the NULL the migration exists to remove — invisible until the imported adventure forked. * The action cap counted a v1 file's turns, and a turn expands into a row per saved attempt, so a file inside the cap could write a multiple of it. * Forking a live node on a borrowed ancestor promoted a sibling on a branch the caller never named. It is a branch switch, and now says so. * Switching attempts left the state, status and memory panels reading the previous take: the story does not change length, so nothing keyed on its length noticed. Same class as the branch-switch bug this phase already fixed. * A retry after switching back numbered the new attempt into the middle of the group instead of the end. * The cursor backfill numbered every action in the table once per adventure; correlated to the adventure being updated, it is an index lookup instead. * Renaming a branch answered own_actions=0. And one behaviour change recorded rather than repaired: script-visible history and actionCount no longer count blank-text rows. That is the right shape and there is no reading compatible with both, so plan/14 says so. 409 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015H5qiyiR7gtFQaoDphHZ3g
This commit is contained in:
committed by
Parth
co-authored by
Claude Opus 5
parent
2d38a162d4
commit
4af6e17406
@@ -404,3 +404,53 @@ def test_export_carries_every_attempt_as_its_own_node(client):
|
||||
ai_rows = [a for a in rows if a.type == "ai"]
|
||||
assert [(a.text, a.live) for a in ai_rows] == [("One.", False), ("Two.", True)]
|
||||
assert len({(a.branch_id, a.depth) for a in ai_rows}) == 1
|
||||
|
||||
|
||||
def test_a_retry_after_switching_back_files_the_new_attempt_last(client):
|
||||
"""The group stays in the order the attempts were made.
|
||||
|
||||
`add_attempt` used to number a new take one past the take it replaced, which
|
||||
is the end of the group only when the story is standing on the newest one.
|
||||
Switch a three-take turn back to the first and retry, and the new attempt
|
||||
collided with take 2 — `renumber` then broke the tie by id and filed it
|
||||
*between* takes 2 and 3, so the pager walked them in an order nobody played.
|
||||
"""
|
||||
ScriptedProvider.replies = ["One.", "Two.", "Three.", "Four."]
|
||||
_play(client)
|
||||
_retry(client)
|
||||
_retry(client)
|
||||
assert [a.text for a in _rows(client.adv_id) if a.type == "ai"] == [
|
||||
"One.", "Two.", "Three."]
|
||||
|
||||
live = _page(client)["actions"][-1]
|
||||
r = client.post(
|
||||
f"/api/adventures/{client.adv_id}/actions/{live['id']}/variant",
|
||||
json={"index": 0})
|
||||
assert r.status_code == 200, r.text
|
||||
|
||||
_retry(client)
|
||||
ai = [a for a in _rows(client.adv_id) if a.type == "ai"]
|
||||
assert [a.text for a in ai] == ["One.", "Two.", "Three.", "Four."]
|
||||
assert [a.variant_index for a in ai] == [0, 1, 2, 3]
|
||||
assert attempts.live_in(ai).text == "Four."
|
||||
|
||||
|
||||
def test_the_adventure_list_quotes_the_take_the_story_tells(client):
|
||||
"""The index screen and the story have to agree.
|
||||
|
||||
Siblings share a depth and the newest of them has the highest id, so a
|
||||
snippet ordered by `(depth, id)` alone quotes whichever attempt was written
|
||||
last — which, after switching back, is the one the player threw away.
|
||||
"""
|
||||
ScriptedProvider.replies = ["One.", "Two."]
|
||||
_play(client)
|
||||
_retry(client)
|
||||
live = _page(client)["actions"][-1]
|
||||
assert live["text"] == "Two."
|
||||
client.post(f"/api/adventures/{client.adv_id}/actions/{live['id']}/variant",
|
||||
json={"index": 0})
|
||||
|
||||
listed = client.get("/api/adventures").json()
|
||||
row = [a for a in listed if a["id"] == client.adv_id][0]
|
||||
assert "One." in row["snippet"]
|
||||
assert "Two." not in row["snippet"]
|
||||
|
||||
Reference in New Issue
Block a user