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:
1 parent
ed5bb074c5
commit
27559dabc4
18 files changed
+2607
-225
No files matched your search
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user