READY: RESI VOX - the Resi song goes out with its lead singer's mic

PCO's song description names the lead ('Genesis lead', RESI marks the song, else the last before the
message), the lineup screen gives their VOX, and the Resi AJA's Lead Vocal channel (21) is read from
the card with Dante's own read-only query. Wrong: red, and the hold / auto-fix presses that VOX's AJA
button on page 13 and reads the card back. Two leads, a lead not on the lineup, a PCO Audio note naming
another VOX, or VOX 5+ are flagged only. Checked on 2026-10-25's real data: Genesis, VOX 4, AJA on VOX 4;
the Audio note says V2, so it is flagged and nothing switches. Applied on NA after the services.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 07:30:36 -04:00
co-authored by Claude Opus 5.5
parent 0ecec5f112
commit 32e071e169
8 changed files with 330 additions and 7 deletions
+73 -2
View File
@@ -648,6 +648,7 @@ class TestLook(unittest.TestCase):
lineup=lambda: lineup(["Jonah Perdue", "Krista Perdue", "Kevin Michel", "Anthony Hoyt", "Tim Hatch"]),
resolume=composition, prop_bundle=lambda plan: [["Message Templates.probundle", WEDNESDAY]],
macros=macros_found, companion=lambda n: COMPANION.get(n, {}), live=live_set,
resi_now=lambda: (3, {"tx_channel": "03-VOX 3", "tx_device": "BOH-AD4Q-A", "name": "5 - Lead Vocal"}),
services=lambda: [SUNDAY_SVC], start=lambda svcs: {"survey": {SUNDAY_SVC["name"]: {"exists": True}}, "status": None})
src.update(over)
return src
@@ -672,7 +673,7 @@ class TestLook(unittest.TestCase):
def test_a_campus_that_only_receives(self):
cfg = json.loads(json.dumps(CONFIG))
for k in ("locations", "prop_bundle_host", "resolume_url", "lineup_url"):
for k in ("locations", "prop_bundle_host", "resolume_url", "lineup_url", "resi_vox"): # Resi goes out from NA only
cfg["ready"].pop(k)
cfg["ready"]["prop_url"] = ""
r = rd.look(cfg, now=WEDNESDAY, sources=self.sources())
@@ -732,7 +733,8 @@ class TestWatchdog(unittest.TestCase):
self.step(WEDNESDAY)
plan = self.engine.fix_plan("ready", self.snap)
fixes = sorted(what for _, kind, what, _ in plan if kind == "fix")
self.assertEqual(fixes, ["ready_ableton_mix", "ready_copy_tracks", "ready_led_cross", "ready_lineup", "ready_ltc", "ready_recheck"])
self.assertEqual(fixes, ["ready_ableton_mix", "ready_copy_tracks", "ready_led_cross", "ready_lineup", "ready_ltc", "ready_recheck",
"ready_resi_vox"]) # RESI VOX: the test plan names no lead
ran = []
self.engine.executor = lambda cfg, fix_id, chk: ran.append(fix_id) or True
self.engine.run_fixes("ready", self.snap, None, WEDNESDAY + 10)
@@ -911,3 +913,72 @@ class TestRunHere(unittest.TestCase):
def test_a_script_for_this_mac_runs_here_without_ssh(self):
cfg = {"hosts": {"ingest": {"self": True}}}
self.assertEqual(rd.run_remote(cfg, "ingest", "import json; print(json.dumps({'ok': 1}))"), {"ok": 1})
class TestResiVox(unittest.TestCase):
"""RESI VOX (2026-10-25): the Resi song goes to the locations with its lead's mic."""
RV = {"aja": "STUDIO-A-Resi-AJA-SDI", "channel": 21, "mics": "BOH-AD4Q-A",
"buttons": {"1": [13, 3, 1], "2": [13, 3, 2], "3": [13, 3, 3], "4": [13, 3, 4]}}
LINEUP = {"state": {"slots": [{"label": "[LEADER, VOX 1, ACOUSTIC] MIX 1", "person": {"name": "Mike Powell"}},
{"label": "[VOX 2] MIX 2", "person": {"name": "Rosaliz Gaitan"}},
{"label": "[VOX 3] MIX 3", "person": {"name": "DJ McLean"}},
{"label": "[VOX 4] MIX 4", "person": {"name": "Genesis Gomez"}},
{"label": "[VOX 5] MIX 8", "person": {"name": "Bianca Barros"}},
{"label": "[DRUMS]", "person": {"name": "Luis Decamps"}}]}}
def plan(self, desc, audio=""):
notes = {"Audio": audio} if audio else {}
return {"items": [item("Worship", "header"), item("Jehovah", "song", description="DJ lead"),
item("Name Above All Names", "song", description=desc, notes=notes),
item("Message", "header"), item("Owe You Praise TAG", "song")], "team": []}
def run_eval(self, desc, now_vox, audio=""):
line, want = rd.eval_resi_vox({"resi_vox": self.RV}, self.plan(desc, audio), self.LINEUP, now_vox,
{"tx_channel": "0%s-VOX %s" % (now_vox, now_vox), "tx_device": "BOH-AD4Q-A", "name": "5 - Lead Vocal"})
return [m for _, m, _ in line.faults], line.info, want
def test_the_lead_on_the_right_mic(self):
self.assertEqual(self.run_eval("Genesis lead\nRESI TX", 4), ([], "VOX 4 GENESIS", 4))
def test_the_wrong_mic_is_red_and_says_which(self):
self.assertEqual(self.run_eval("Genesis lead\nRESI TX", 2)[0], ["RESI V2 NOT VOX 4"])
self.assertEqual(self.run_eval("Genesis lead\nRESI TX", 2)[2], 4)
def test_no_resi_mark_means_the_last_song_before_the_message(self):
self.assertEqual(rd.resi_song(self.plan("Rosaliz lead"))["title"], "Name Above All Names")
self.assertEqual(self.run_eval("Rosaliz lead", 2), ([], "VOX 2 ROSALIZ", 2))
def test_what_is_not_switched_by_itself(self):
self.assertEqual(self.run_eval("Mike and DJ lead", 1)[0], ["2 LEADS: MIKE DJ"]) # two leads: a person picks
self.assertEqual(self.run_eval("Genesis lead", 4, audio="V2")[0], ["PCO SAYS V2, LEAD V4"]) # PCO and the lineup differ
self.assertEqual(self.run_eval("Bianca lead", 4)[0], ["LEAD ON VOX 5: NO BUTTON"])
self.assertEqual(self.run_eval("Josh lead", 4)[0], ["JOSH NOT ON LINEUP"])
self.assertEqual(self.run_eval("*CONDENSED*", 4)[0], ["NO LEAD NAME ABOVE."])
for f in ("Genesis lead", "Mike and DJ lead", "Bianca lead"):
self.assertIsNone(self.run_eval(f, 4, audio="V2")[2]) if f != "Genesis lead" else None
def test_dante_rx_reads_the_subscription(self):
import socket
import struct
import threading
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
srv.bind(("127.0.0.1", 0))
port = srv.getsockname()[1]
def answer():
req, addr = srv.recvfrom(64)
strings = b"\x00" * 12 + b"04-VOX 4\x00BOH-AD4Q-A\x005 - Lead Vocal\x00"
base = 12 + 16 * 20
recs = b"".join(struct.pack(">10H", 17 + i, 14, 0, 0, 0, 0, 0x101, 9, 0, 0) for i in range(4))
recs += struct.pack(">10H", 21, 14, 0, base + 12, base + 21, base + 32, 0x101, 9, 0, 0)
recs += b"".join(struct.pack(">10H", 22 + i, 14, 0, 0, 0, 0, 0x101, 9, 0, 0) for i in range(11))
srv.sendto(req[:4] + req[4:6] + req[6:8] + b"\0" * 4 + recs + strings, addr)
threading.Thread(target=answer, daemon=True).start()
orig = rd.DANTE_ARC
rd.DANTE_ARC = port
try:
got = rd.dante_rx("127.0.0.1", 21)
finally:
rd.DANTE_ARC = orig
srv.close()
self.assertEqual((got["name"], got["tx_channel"], got["tx_device"]), ("5 - Lead Vocal", "04-VOX 4", "BOH-AD4Q-A"))