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>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-04 17:07:24 +01:00
1 parent ed5bb074c5
commit 27559dabc4
18 files changed
+2607 -225

No files matched your search

+34
View File
@@ -105,6 +105,19 @@ class TestWatchCommands(unittest.TestCase):
self._stage("warn")
self.assertEqual(self._run("block", stdin=self._bash("make compose-deploy")).returncode, 0)
def test_block_heavy_jobs_while_a_mayday_holds_radio_silence(self):
self._stage("ok")
self.root.mkdir(exist_ok=True)
silence = self.root / "radio-silence.json"
now = time.time()
silence.write_text(json.dumps({"since": now, "until": now + 60, "by": "One", "text": "fire"}), encoding="utf-8")
blocked = self._run("block", stdin=self._bash("make compose-deploy"))
self.assertEqual(blocked.returncode, 2)
self.assertIn("mayday by One", blocked.stderr)
self.assertEqual(self._run("block", stdin=self._bash("make swarm-down")).returncode, 0)
silence.write_text(json.dumps({"since": now - 120, "until": now - 60, "by": "One", "text": "fire"}), encoding="utf-8")
self.assertEqual(self._run("block", stdin=self._bash("make compose-deploy")).returncode, 0)
def test_block_ignores_heavy_words_inside_quotes_and_heredocs(self):
self.cfg["heavy_commands"] = [r"\bmake\s+(-C\s+\S+\s+)?(compose-deploy|test|swarm-\S+)(\s|$)"]
self._write_config()
@@ -152,6 +165,27 @@ class TestWatchCommands(unittest.TestCase):
self.assertEqual(hook("s1"), "")
self.assertIn("RESOURCE WARN", hook("s2"))
def test_watch_keeps_each_new_queue_history_row_once(self):
self.cfg["thresholds_gb"] = {"warn": 0, "stop": 0, "critical": 0}
self._write_config()
self.root.mkdir()
source = self.root / "queue-history.jsonl"
kept = self.base / ".local" / "state" / "agents-council" / "queue-history.jsonl"
kept.parent.mkdir(parents=True)
kept.write_text('{"id": "old"}\n', encoding="utf-8")
source.write_text('{"id": "old"}\n{"id": "a"}\n{broken\n', encoding="utf-8")
payload = json.dumps({"session_id": "s1", "hook_event_name": "PostToolUse"})
def kept_ids() -> list[str]:
self.assertEqual(self._run("watch", stdin=payload).returncode, 0)
return [json.loads(line)["id"] for line in kept.read_text(encoding="utf-8").splitlines()]
self.assertEqual(kept_ids(), ["old", "a"])
self.assertEqual(kept_ids(), ["old", "a"])
with source.open("a", encoding="utf-8") as handle:
handle.write('{"id": "b"}\n')
self.assertEqual(kept_ids(), ["old", "a", "b"])
def test_cache_trim_evicts_least_recently_used_first(self):
old, new = self.cache / "old", self.cache / "new"
old.write_bytes(b"x" * 4096)