import inspect
from pathlib import Path
from PIL import Image
import app
from sovereign_bench.models import AgentTurn, EvidenceItem, JurorVote, TrialEvent
OLD_CARD_CLASSES = [
"paper-panel",
"juror-panel",
"mind-panel",
"empty-state",
"trial-downloads",
]
def _event_with_lower_tab_data() -> TrialEvent:
evidence = EvidenceItem(
id="E1",
title="Ledger entry",
source="Archive",
excerpt="A short exhibit excerpt.",
supports="claimant",
reliability=0.82,
note="Useful but incomplete.",
)
vote = JurorVote(
juror="Karl Marx",
persona=app.JUROR_PERSONAS["Karl Marx"],
vote="liable",
reason="The exhibit supports the claim.",
evidence_ids=["E1"],
)
return TrialEvent(
phase="deliberation",
title="Jury weighs the record",
body="The jury reviews the record.",
turns=[
AgentTurn(
agent="Nemotron Jury",
role="juror panel",
content="The jurors compare E1 and state their votes.",
model="nvidia/Nemotron-Orchestrator-8B",
confidence=0.84,
input="SYSTEM:\nYou are the jury.\n\nUSER:\nWeigh E1 and explain the vote.",
)
],
evidence=[evidence],
votes=[vote],
trace={"mode": "test"},
)
def _speaker_event(agent: str, phase: str = "questions") -> TrialEvent:
return TrialEvent(
phase=phase,
title=f"{agent} speaks",
body="A single speaker takes the floor.",
turns=[
AgentTurn(
agent=agent,
role="test speaker",
content=f"{agent} has the visible floor.",
model="test-model",
confidence=0.9,
input="SYSTEM:\nTest prompt.",
)
],
)
def test_lower_tab_renderers_emit_plain_text_classes():
event = _event_with_lower_tab_data()
html = "\n".join(
[
app.render_evidence([]),
app.render_evidence([event]),
app.render_jurors([]),
app.render_jurors([event]),
app.render_mind([], True),
app.render_mind([event], True),
app.render_mind([event], False),
]
)
for class_name in OLD_CARD_CLASSES:
assert class_name not in html
assert "drawer-text-block" in html
assert "drawer-empty" in html
assert "mind-text" in html
def test_download_controls_are_not_wired_into_app():
source = inspect.getsource(app.build_app)
assert "DownloadButton" not in source
assert "Download decree" not in source
assert "Download agent trace" not in source
def test_courtroom_splits_six_jurors_between_side_benches():
html = app.render_court([_event_with_lower_tab_data()], started=True)
assert "jury-benches left" in html
assert "jury-benches right" in html
assert html.count("Input" in html
assert "tooltip-io-label'>Output" in html
assert "Click to open full thread" in html
assert "class='ai-thread-modal'" in html
assert "thread-block'>SYSTEM:" in html
assert "The jurors compare E1 and state their votes." in html
assert "href='#ai-thread-karl-marx'" in html
def test_courtroom_renders_historical_judge_and_juror_assets():
html = app.render_court([_event_with_lower_tab_data()], started=True)
assert "Marcus Aurelius" in html
assert "assets/characters/marcus-aurelius.png" in html
for name, image in app.JUROR_IMAGES.items():
assert name in html
assert image in html
assert html.count("class='juror-portrait'") == 6
def test_courtroom_renders_foreground_fences_and_judge_table_above_characters():
html = app.render_court([_event_with_lower_tab_data()], started=True)
assert html.count("assets/foreground/foregroundFence.png") == 2
assert "assets/foreground/JudgeTable.png" in html
assert html.find("class='puppet judge") < html.find("class='foreground-props'")
assert ".foreground-props {\n position: absolute;\n inset: 0;\n z-index: 13;" in app.CSS
assert ".puppet {\n --skin: #c99257;" in app.CSS
assert "z-index: 8;" in app.CSS
def test_foreground_prop_assets_have_real_transparency():
for path in [
Path("assets/foreground/foregroundFence.png"),
Path("assets/foreground/JudgeTable.png"),
]:
alpha = Image.open(path).convert("RGBA").getchannel("A")
histogram = alpha.histogram()
assert histogram[0] > 0, f"{path} has no fully transparent pixels"
assert histogram[255] > 0, f"{path} has no fully opaque prop pixels"
def test_latest_speaker_sets_stage_class_and_speech_bubble():
html = app.render_court([_speaker_event("Advocate Auric", phase="claims")], started=True)
assert "speaker-auric" in html
assert "class='speech-bubble'" in html
assert "Advocate Auric has the visible floor." in html
assert "puppet auric active walking" in html
assert "puppet sable active" not in html
def test_individual_juror_can_be_active_speaker():
event = TrialEvent(
phase="deliberation",
title="Juror Karl Marx Votes",
body=app.JUROR_PERSONAS["Karl Marx"],
turns=[
AgentTurn(
agent="Karl Marx",
role="juror",
content="Liable. E1 carries the record.",
model="nvidia/Nemotron-Orchestrator-8B",
confidence=0.86,
input="SYSTEM:\nJury JSON prompt.",
)
],
votes=[
JurorVote(
juror="Karl Marx",
persona=app.JUROR_PERSONAS["Karl Marx"],
vote="liable",
reason="E1 carries the record.",
evidence_ids=["E1"],
)
],
)
html = app.render_court([event], started=True)
assert "speaker-karl-marx" in html
assert "