sea select scroll log: publish a Role<->Celtic-position affinity on the completing draw; redact + relinquish on DEL; re-publish on re-draw — TDD

The gameroom DRAW SEA phase now writes drama provenance, mirroring sig/sky. When a gamer's 6-card Celtic Cross COMPLETES, a SEA_DRAWN Scroll log publishes their affinity with the card sitting in their Role-correlated position.

- epic/views.py: ROLE_POSITION_MAP — the user's sixfold index (PC->crown, NC->leave, EC->loom, SC->cover, AC->cross, BC->lay; roles rotate each round, so a seat's CURRENT role drives it) + SEA_POSITION_LABELS (each spread's display label for a position KEY; Waite-Smith's Behind/Before/Beneath + Escape-Velocity's Leave/Loom/Lay both key to the same index). sea_save publishes SEA_DRAWN on the <6->6 completing transition only (a reload that re-POSTs the full hand can't double-publish); a re-draw first redacts the standing relinquishment, then publishes anew. sea_delete redacts the published affinity (the strikethrough) + records SEA_RELINQUISHED in its wake (the redact-pair). _sea_affinity_for mirrors SIG_READY's polarity-qualified name_title + corner abbrev; _redact_standing_sea_event tests 'not retracted' in PYTHON (the SQLite JSONField exclude-NULL trap).

- drama/models.py: SEA_DRAWN + SEA_RELINQUISHED verbs + to_prose ('draws {poss} Celtic Cross, finding affinity with the {card}{abbrev} in the {Position}.' / 'relinquishes {poss} affinity with the {card}.'); 'The ' stripped so a levity/gravity qualifier butts the proper name. The generic struck/retracted property renders the strikethrough + data-label=redact in _scroll.html unchanged.

TDD: 4 drama prose ITs (affinity statement, spread-label passthrough, relinquishment, struck-when-retracted) + 7 epic ITs (publish-on-complete, position=crown for PC, none-before-complete, no-double-publish-on-reload, DEL redacts+relinquishes, re-draw redacts-the-relinquishment+republishes, DEL-noop-when-nothing-published). 459 drama + epic-view ITs green.

- bundled (parallel work): rootvars.scss --sixUser/--sepUser/--octUser slot reassignments across the forest/khaki/blade palettes (tuning the new reelhouse h2 bgs) + a new --terMrb.

[[project-sea-select-scroll-provenance]] [[feedback-jsonfield-exclude-sqlite-null]]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-06-08 19:35:45 -04:00
parent 039152a787
commit d28046f3da
5 changed files with 228 additions and 9 deletions

View File

@@ -26,6 +26,12 @@ class GameEvent(models.Model):
SIG_UNREADY = "sig_unready"
# Sky Select phase
SKY_SAVED = "sky_saved"
# Sea Select phase — Role↔Celtic-position affinity provenance. SEA_DRAWN
# publishes on hand-complete; SEA_RELINQUISHED is the let-go entry left in
# the redact-pair's wake when the spread is DELeted (the SEA_DRAWN gets
# `retracted` + a re-draw later retracts the relinquishment in turn).
SEA_DRAWN = "sea_drawn"
SEA_RELINQUISHED = "sea_relinquished"
VERB_CHOICES = [
(ROOM_CREATED, "Room created"),
@@ -40,6 +46,8 @@ class GameEvent(models.Model):
(SIG_READY, "Sig claim staked"),
(SIG_UNREADY, "Sig claim withdrawn"),
(SKY_SAVED, "Sky saved"),
(SEA_DRAWN, "Sea drawn"),
(SEA_RELINQUISHED, "Sea relinquished"),
]
room = models.ForeignKey(
@@ -154,6 +162,31 @@ class GameEvent(models.Model):
f"beholds the skyscape of {poss} birth, "
f"which yields {obj} equal {joined} capacities."
)
if self.verb == self.SEA_DRAWN:
# Affinity statement: the card drawn into the gamer's Role-correlated
# Celtic position. `card_name`/`corner_rank`/`suit_icon` mirror
# SIG_READY (qualifier-prefixed name + abbrev, "The " stripped so a
# qualifier butts the proper name); `position_label` is the
# spread-specific label for the role's position key.
card_name = d.get("card_name", "a card")
corner_rank = d.get("corner_rank", "")
suit_icon = d.get("suit_icon", "")
position_label = d.get("position_label", "spread")
if corner_rank:
icon_html = f' <i class="fa-solid {suit_icon}"></i>' if suit_icon else ""
abbrev = f" ({corner_rank}{icon_html})"
else:
abbrev = ""
card_name = card_name.replace("The ", "", 1)
_, _, poss = _actor_pronouns(self.actor)
return (
f"draws {poss} Celtic Cross, finding affinity with "
f"the {card_name}{abbrev} in the {position_label}."
)
if self.verb == self.SEA_RELINQUISHED:
card_name = d.get("card_name", "a card").replace("The ", "", 1)
_, _, poss = _actor_pronouns(self.actor)
return f"relinquishes {poss} affinity with the {card_name}."
return self.verb
@property