Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions executive/watchdog.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,23 @@ def run_checks() -> tuple[bool, list[str]]:

for a in actions:
rid = a.get("ruling_id", "")
if rid and rid not in approved and "override" not in a.get("action_type", "").lower():
# Action references ruling not in judicial log
proof = a.get("override_proof")
if not rid:
continue
action_type = a.get("action_type", "").lower()
proof = a.get("override_proof")
is_override = "override" in action_type

if is_override:
if not proof:
alerts.append(
f"Action without judicial approval and no override proof: ruling_id={rid}"
f"Override action without cryptographic proof: ruling_id={rid}"
)
continue

if rid not in approved and not proof:
alerts.append(
f"Action without judicial approval and no override proof: ruling_id={rid}"
)

# (c) Override frequency
overrides = [a for a in actions if a.get("override_proof")]
Expand Down
44 changes: 29 additions & 15 deletions litigation/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,29 @@ def _collect_transcripts() -> List[Tuple[Path, str, str]]:
return out


def _matches_transcript_query(name: str, path: Path) -> bool:
"""Match transcript by exact name, prefix, or topic suffix (not arbitrary substring)."""
stem = path.stem
if stem == name or path.name == name:
return True
if stem.startswith(name):
return True
return stem.endswith(f"-{name}") or stem.endswith(f"_{name}")


def _resolve_transcript(name: str, rows: List[Tuple[Path, str, str]]) -> Optional[Path]:
"""Pick the best transcript path for a query name."""
matches = [r for r in rows if _matches_transcript_query(name, r[0])]
if not matches:
return None
exact = [m for m in matches if m[0].stem == name or m[0].name == name]
if exact:
return exact[0][0]
if len(matches) > 1:
matches.sort(key=lambda r: len(r[0].stem))
return matches[0][0]


def cmd_list(plain: bool) -> None:
"""List transcripts in a table or plain lines."""
rows = _collect_transcripts()
Expand Down Expand Up @@ -116,17 +139,11 @@ def cmd_show(name: Optional[str], pager: bool) -> None:
path = rows[0][0]
else:
name = name.strip()
matches = [r for r in rows if r[0].stem == name or r[0].name == name or r[0].stem.startswith(name) or name in r[0].stem]
if not matches:
path = _resolve_transcript(name, rows)
if path is None:
print(f"No transcript matching '{name}'", file=sys.stderr)
print("Use 'viewer.py list' to see available transcripts.", file=sys.stderr)
sys.exit(1)
if len(matches) > 1:
# Prefer exact stem match
exact = [m for m in matches if m[0].stem == name]
path = exact[0][0] if exact else matches[0][0]
else:
path = matches[0][0]
text = path.read_text(encoding="utf-8")
if pager:
import subprocess
Expand Down Expand Up @@ -223,15 +240,12 @@ def do_GET(self) -> None:
if not name:
self.send_error(404)
return
found = None
for p, date_str, topic in rows:
if p.stem == name or name in p.stem:
found = (p, topic)
break
if not found:
resolved = _resolve_transcript(name, rows)
if not resolved:
self.send_error(404)
return
p, topic = found
p = resolved
topic = next(t for path, _, t in rows if path == p)
md = p.read_text(encoding="utf-8")
body_html = _markdown_to_html(md)
title = f"In Re: {topic}"
Expand Down
23 changes: 23 additions & 0 deletions tests/test_executive_watchdog.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,29 @@ def test_run_checks_passes_with_approved_ruling(tmp_path, monkeypatch: pytest.Mo
assert alerts == []


def test_run_checks_alerts_on_override_without_proof(
tmp_path, monkeypatch: pytest.MonkeyPatch
) -> None:
log_path = tmp_path / "judicial_decisions.log"
actions_path = tmp_path / "executive_actions.log"

JudicialLog(log_path=log_path) # empty approvals

_write_action(
actions_path,
action_type="override",
ruling_id="2026-UNKNOWN-999",
description="rogue override",
)

monkeypatch.setattr(watchdog, "JudicialLog", lambda: JudicialLog(log_path=log_path))
monkeypatch.setattr(watchdog, "_actions_log_path", lambda: actions_path)

ok, alerts = watchdog.run_checks()
assert ok is False
assert any("Override action without cryptographic proof" in a for a in alerts)


def test_run_checks_alerts_on_unapproved_action_without_proof(
tmp_path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down
17 changes: 16 additions & 1 deletion tests/test_litigation_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pathlib import Path

from litigation.viewer import _collect_transcripts, _transcript_meta
from litigation.viewer import _collect_transcripts, _resolve_transcript, _transcript_meta


def test_transcript_meta_iso_date_slug() -> None:
Expand Down Expand Up @@ -34,3 +34,18 @@ def test_collect_transcripts_skips_readme_and_dotfiles(tmp_path: Path, monkeypat
assert rows[0][0].name == "2026-01-01-sample.md"
assert rows[0][1] == "2026-01-01"
assert rows[0][2] == "Sample"


def test_resolve_transcript_prefers_exact_suffix_over_broader_match(tmp_path: Path) -> None:
"""Short topic queries must not match unrelated longer stems (e.g. bar vs foo-bar)."""
bar = tmp_path / "2026-06-01-bar.md"
foo_bar = tmp_path / "2026-06-01-foo-bar.md"
bar.write_text("# bar", encoding="utf-8")
foo_bar.write_text("# foo bar", encoding="utf-8")

rows = [
(foo_bar, "2026-06-01", "Foo Bar"),
(bar, "2026-06-01", "Bar"),
]
resolved = _resolve_transcript("bar", rows)
assert resolved == bar