Uncommit feature in character creation

Players can now unlock and revise any reasonably revisable choice while
character creation is ongoing: basic Rat Records (also in recruit
creation), submitted techniques (until the swap phase begins), J/Q/K
face-card assignments (until everyone is ready), and Like/Hate answers
written for crewmates. New endpoints unsubmit-techniques and
unassign-face-techniques clear the locked-in state server-side so the
phase machine waits for the revision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 17:05:52 -07:00
parent ba32c96b7b
commit 952b1be872
6 changed files with 224 additions and 14 deletions

View File

@@ -1124,6 +1124,84 @@ def test_submit_techniques_rejects_collisions(session):
session.refresh(game)
assert game.phase == "swap_techniques"
def test_unsubmit_techniques(session):
game = crud.create_game(session)
p1 = crud.add_player(session, game.id, "P1", is_creator=True)
p2 = crud.add_player(session, game.id, "P2")
game.phase = "character_creation"
session.add(game)
session.commit()
# Nothing to unlock before submitting
ok, _ = crud.unsubmit_techniques(session, p1.id)
assert not ok
err = crud.submit_techniques(session, p1.id, "Tail Whip", "Pocket Sand", "Cheese Decoy")
assert err is None
# Unlock clears the submission so it can be revised
ok, msg = crud.unsubmit_techniques(session, p1.id)
assert ok, msg
session.refresh(p1)
assert json.loads(p1.created_techniques) == []
# Resubmitting (with everyone else done) still triggers the swap
err = crud.submit_techniques(session, p2.id, "Plank Pirouette", "Bilge Bomb", "Rat King Roar")
assert err is None
err = crud.submit_techniques(session, p1.id, "Tail Whip", "Pocket Sand", "Cheese Decoy")
assert err is None
session.refresh(game)
assert game.phase == "swap_techniques"
# Once swapping has begun, revision is locked out
ok, _ = crud.unsubmit_techniques(session, p1.id)
assert not ok
def test_unassign_face_techniques(session):
game, p1, p2, p3 = make_swap_game(session)
crud.auto_assign_techniques(session, game)
session.refresh(game)
assert game.phase == "scene_setup"
# Wrong phase is rejected
ok, _ = crud.unassign_face_techniques(session, p1.id)
assert not ok
# Fresh game stopped in assign_techniques: two assign, one revises
game, p1, p2, p3 = make_swap_game(session)
game.phase = "assign_techniques"
for p in (p1, p2, p3):
p.swapped_techniques = p.created_techniques
session.add(p)
session.add(game)
session.commit()
# Not assigned yet -> nothing to unlock
ok, _ = crud.unassign_face_techniques(session, p1.id)
assert not ok
t1 = json.loads(p1.swapped_techniques)
ok, msg = crud.assign_face_card_techniques(session, p1.id, t1[0], t1[1], t1[2])
assert ok, msg
ok, msg = crud.unassign_face_techniques(session, p1.id)
assert ok, msg
session.refresh(p1)
assert not p1.tech_jack and not p1.tech_queen and not p1.tech_king
assert not p1.is_ready
# Others finishing while p1 is revising must not advance the game
for p in (p2, p3):
t = json.loads(p.swapped_techniques)
ok, msg = crud.assign_face_card_techniques(session, p.id, t[0], t[1], t[2])
assert ok, msg
session.refresh(game)
assert game.phase == "assign_techniques"
# p1 reassigns (swapped order) and the game moves on
ok, msg = crud.assign_face_card_techniques(session, p1.id, t1[2], t1[0], t1[1])
assert ok, msg
session.refresh(game)
assert game.phase == "scene_setup"
def make_swap_game(session):
"""Helper: 3 players in the swap_techniques phase, each holding their own techniques."""
game = crud.create_game(session)