SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed

- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-04 01:57:35 -04:00
parent 5413e63585
commit c9563308d8
7 changed files with 238 additions and 53 deletions

View File

@@ -2075,6 +2075,86 @@ class NatusSaveViewTest(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTrue(response.json()["confirmed"])
def test_confirm_records_sky_saved_event_with_top_capacitors(self):
"""When action=confirm, log a SKY_SAVED GameEvent w. the highest-count
capacitor name(s) so the billscroll can render the new prose."""
from apps.drama.models import GameEvent
chart = {
"elements": {
# Earthman uses 6 elements; canonical names map to capacitors:
# Fire→Ardor Stone→Ossum Air→Pneuma Water→Humor Time→Tempo Space→Nexus.
"Fire": 3,
"Stone": 1,
"Air": 2,
"Water": 0,
"Time": 1,
"Space": 1,
}
}
self._post({
"birth_dt": "1990-06-15T09:00:00Z",
"birth_lat": 51.5, "birth_lon": -0.1,
"birth_place": "", "house_system": "O",
"chart_data": chart, "action": "confirm",
})
event = GameEvent.objects.get(room=self.room, verb=GameEvent.SKY_SAVED)
self.assertEqual(event.actor, self.user)
self.assertEqual(event.data.get("top_capacitors"), ["Ardor"])
def test_confirm_records_sky_saved_event_with_two_way_tie(self):
from apps.drama.models import GameEvent
chart = {
"elements": {
"Fire": 3, "Stone": 3, # tied at top
"Air": 2, "Water": 0, "Time": 1, "Space": 1,
}
}
self._post({
"birth_dt": "1990-06-15T09:00:00Z",
"birth_lat": 51.5, "birth_lon": -0.1,
"birth_place": "", "house_system": "O",
"chart_data": chart, "action": "confirm",
})
event = GameEvent.objects.get(room=self.room, verb=GameEvent.SKY_SAVED)
# Order follows the canonical ELEMENT_ORDER (Fire, Stone, Time, Space, Air, Water)
self.assertEqual(event.data.get("top_capacitors"), ["Ardor", "Ossum"])
def test_save_without_confirm_does_not_record_sky_saved_event(self):
from apps.drama.models import GameEvent
self._post({
"birth_dt": "1990-06-15T09:00:00Z",
"birth_lat": 51.5, "birth_lon": -0.1,
"birth_place": "", "house_system": "O",
"chart_data": {"elements": {"Fire": 3}},
# no action=confirm — just a draft save
})
self.assertFalse(
GameEvent.objects.filter(room=self.room, verb=GameEvent.SKY_SAVED).exists()
)
def test_confirm_with_dict_shaped_elements_extracts_count(self):
"""Some chart payloads enrich each element to {count, contributors};
natus_save should read .count rather than treating the dict as a value."""
from apps.drama.models import GameEvent
chart = {
"elements": {
"Fire": {"count": 4, "contributors": ["Sun", "Mars", "Jupiter", "Pluto"]},
"Stone": {"count": 1, "contributors": ["Venus"]},
"Air": {"count": 2, "contributors": ["Mercury", "Uranus"]},
"Water": {"count": 0, "contributors": []},
"Time": {"count": 1, "stellia": ["Saturn"]},
"Space": {"count": 1, "parades": ["Neptune"]},
}
}
self._post({
"birth_dt": "1990-06-15T09:00:00Z",
"birth_lat": 51.5, "birth_lon": -0.1,
"birth_place": "", "house_system": "O",
"chart_data": chart, "action": "confirm",
})
event = GameEvent.objects.get(room=self.room, verb=GameEvent.SKY_SAVED)
self.assertEqual(event.data.get("top_capacitors"), ["Ardor"])
def test_confirm_copies_seat_significator_to_character(self):
"""natus_save with action=confirm copies seat.significator onto Character."""
earthman, _ = DeckVariant.objects.get_or_create(