Files
skills/tests/test_news.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 27559dabc4 feat(org): kvb-org-radio and kvb-org-queue replace chair and peer messages
kvb-org-radio is the default channel to other agents: broadcast and direct reach the receiver on its next prompt, security and pan-pan mid-turn through a PostToolBatch hook, and only mayday or direct --wake wake a session, through the receiver's own asyncRewake Stop hook (a SendMessage fallback is printed for sessions without a listener). It keeps the news.jsonl store and lock, so news.py stays a forwarding shim. A mayday holds radio silence for two hours: watch.py blocks new heavy commands and the operator gets a desktop notice. hotfix.py announces merged hotfixes as security items.

kvb-org-queue grants heavy jobs by rule instead of chair messages: queue.py hold waits under a flock with disk reservation, hotfix-first and register rank order, slot limits, stage and silence checks, then runs the command and releases. Every job is recorded; history.py learns disk need per command and slot limits per kind (p90, five runs), watch.py mirrors the history to ~/.local/state. enable-plugins.js registers the new hooks behind a file check so a missing script never blocks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 17:07:24 +01:00

85 lines
3.7 KiB
Python

"""Exercise the news.py shim: it forwards post, hook and show to radio.py, and a missing radio.py never blocks a prompt."""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
NEWS = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "news.py"
class TestNews(unittest.TestCase):
def setUp(self):
tmp = tempfile.TemporaryDirectory()
self.addCleanup(tmp.cleanup)
self.tmp = Path(tmp.name)
self.home = self.tmp / "home"
self.root = self.tmp / "council"
sessions = self.home / ".claude" / "sessions"
sessions.mkdir(parents=True)
for sid, name in (("s1", "One"), ("s2", "Two"), ("s3", "Three")):
(sessions / f"{sid}.json").write_text(json.dumps({"sessionId": sid, "name": name}), encoding="utf-8")
def _exec(self, news: Path, sid: str | None, stdin: str, args: tuple[str, ...]) -> subprocess.CompletedProcess:
env = {
"PATH": "/usr/bin:/bin",
"HOME": str(self.home),
"AGENTS_COUNCIL_ROOT": str(self.root),
"AGENTS_COUNCIL_CONFIG": str(self.tmp / "config.json"),
}
if sid:
env["CLAUDE_CODE_SESSION_ID"] = sid
return subprocess.run(
[sys.executable, str(news), *args], input=stdin, capture_output=True, text=True, env=env, check=False
)
def _cli(self, sid: str | None, *args: str) -> subprocess.CompletedProcess:
return self._exec(NEWS, sid, "", args)
def _hook(self, sid: str) -> list[str]:
result = self._exec(NEWS, None, json.dumps({"session_id": sid, "prompt": "x"}), ("hook",))
self.assertEqual(result.returncode, 0, result.stderr)
return [line.split(" ", 3)[3] for line in result.stdout.splitlines()]
def test_every_session_sees_a_post_once_except_its_author(self):
self.assertEqual(self._cli("s1", "post", "core #1358 merged").stdout, "sent broadcast to all\n")
self.assertEqual(self._hook("s2"), ["One: core #1358 merged"])
self.assertEqual(self._hook("s2"), [])
self.assertEqual(self._hook("s3"), ["One: core #1358 merged"])
self.assertEqual(self._hook("s1"), [])
def test_addressed_post_reaches_only_its_target(self):
self._cli("s1", "post", "your gate is granted", "--to", "Two")
self.assertEqual(self._hook("s3"), [])
self.assertEqual(self._hook("s2"), ["One to Two: your gate is granted"])
self.assertEqual(len(self._cli("s1", "show", "--all").stdout.splitlines()), 1)
def test_hook_never_fails_and_post_needs_a_session(self):
self.assertEqual(self._exec(NEWS, None, "not json", ("hook",)).returncode, 0)
self.assertEqual(self._exec(NEWS, None, "{}", ("hook",)).stdout, "")
self.assertEqual(self._cli(None, "post", "x").returncode, 2)
self.assertEqual(self._cli("s1", "post", " ").returncode, 2)
def test_without_radio_the_hook_passes_and_the_rest_fail(self):
lone = self.tmp / "skills" / "kvb-org-organize" / "scripts" / "news.py"
lone.parent.mkdir(parents=True)
shutil.copy(NEWS, lone)
hook = self._exec(lone, None, json.dumps({"session_id": "s2"}), ("hook",))
self.assertEqual((hook.returncode, hook.stdout), (0, ""))
for args in (("post", "x"), ("show",)):
with self.subTest(args=args):
result = self._exec(lone, "s1", "", args)
self.assertEqual(result.returncode, 2)
self.assertIn("kvb-org-radio", result.stderr)
self.assertFalse(self.root.exists())
if __name__ == "__main__":
unittest.main()