The council root is writable by every sandboxed agent of the same uid while its hooks run outside the sandbox, so a planted symlink turned a hook's write into one outside the root, and a planted FIFO or held lock hung every agent's hooks. - Add nofollow.py: open, read, lock, append, replace and unlink below a root through O_NOFOLLOW directory fds, O_NONBLOCK and regular files only; reads stop at 64 MiB and replace undecodable bytes; locks take an explicit wait. - watch, queue, radio and council read and write everything below the council root through it; keep_rows copies no symlinked file into ~/.local/state. - watch: read_json returns dicts only, alert lines that are no alert (deep nesting included) are skipped, cmd_cleanup makes and removes its lock through a root dir fd, and the cache trim walks with fwalk, counts allocated blocks and each inode once. - radio: the hooks wait at most 2 s for a session lock and skip the delivery past it. - council: read_claim ignores claims that are no object with session, runtime and beat, so a malformed claim no longer disables the claim gate. MEDUSA acceptances (kvb-think-criticize 2026-11-06, skeptics A and B): MM-SCAN-058 on nofollow.replace callers, B105 and HPT006 in history.py, RT-SCAN-054 in council.py (name matches). 🎯 Fix: 92 % 💥 Break: 4 % 💸 Cost: 100 % ⏱️ Time: 100 % 🛡️ Security: 100 % 💰 Value: ? 🏆 Score: 88 🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
423 lines
19 KiB
Python
423 lines
19 KiB
Python
"""Exercise the kvb-org-organize resource watch: stages, projection, block, admit, alerts, caches."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
WATCH = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "watch.py"
|
|
LIMITS = {"warn": 40, "stop": 15, "critical": 12}
|
|
|
|
|
|
def _load():
|
|
spec = importlib.util.spec_from_file_location("watch", WATCH)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class TestStage(unittest.TestCase):
|
|
def setUp(self):
|
|
self.watch = _load()
|
|
self.cfg = {"thresholds_gb": LIMITS, "projection_minutes": 30}
|
|
|
|
def test_thresholds(self):
|
|
now = time.time()
|
|
for free, stage in ((50, "ok"), (30, "warn"), (14, "stop"), (5, "critical")):
|
|
self.assertEqual(self.watch.stage_for(free, [], self.cfg, now), stage)
|
|
|
|
def test_cpu_and_ram_percentages(self):
|
|
self.assertEqual(self.watch.cpu_busy_percent((100, 40), (200, 60)), 80.0)
|
|
meminfo = "MemTotal: 1000 kB\nMemFree: 100 kB\nMemAvailable: 250 kB\n"
|
|
self.assertEqual(self.watch.mem_used_percent(meminfo), 75.0)
|
|
|
|
def test_fast_fall_escalates_one_stage(self):
|
|
now = time.time()
|
|
self.assertEqual(self.watch.stage_for(32, [[now - 600, 48]], self.cfg, now), "stop")
|
|
self.assertEqual(self.watch.stage_for(32, [[now - 600, 32.1]], self.cfg, now), "warn")
|
|
|
|
|
|
class TestWatchCommands(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.tmp.cleanup)
|
|
self.base = Path(self.tmp.name)
|
|
self.root = self.base / "council"
|
|
self.config = self.base / "config.json"
|
|
self.cache = self.base / "cache"
|
|
self.cache.mkdir()
|
|
self.cfg = {
|
|
"disk": str(self.base),
|
|
"thresholds_gb": LIMITS,
|
|
"projection_minutes": 30,
|
|
"interval_seconds": 0,
|
|
"cache_interval_seconds": 86400,
|
|
"heavy_commands": ["^make compose-deploy", "^make swarm-"],
|
|
"never_block": ["^make swarm-down"],
|
|
"cleanup": [],
|
|
"worktree_cleanup": [],
|
|
"worktrees_command": "true",
|
|
"worktree_marker": "Makefile",
|
|
"busy_processes": ["sleep"],
|
|
"cache_dirs": [str(self.cache)],
|
|
"cache_budget_percent": 3,
|
|
"slot_need_gb": {"swarm": 30, "test": 0.001},
|
|
"notify": ["true"],
|
|
"load_limits": {"cpu_percent": 101, "mem_percent": 101, "sample_seconds": 0.1, "kinds": ["test"]},
|
|
}
|
|
self._write_config()
|
|
|
|
def _write_config(self):
|
|
self.config.write_text(json.dumps(self.cfg), encoding="utf-8")
|
|
|
|
def _run(self, *args: str, stdin: str = "") -> subprocess.CompletedProcess:
|
|
env = {
|
|
"PATH": str(self.base),
|
|
"HOME": str(self.base),
|
|
"AGENTS_COUNCIL_ROOT": str(self.root),
|
|
"AGENTS_COUNCIL_CONFIG": str(self.config),
|
|
}
|
|
return subprocess.run(
|
|
[sys.executable, str(WATCH), *args],
|
|
capture_output=True, text=True, env=env, input=stdin, check=False,
|
|
)
|
|
|
|
def _stage(self, stage: str) -> None:
|
|
self.root.mkdir(exist_ok=True)
|
|
(self.root / "watch.json").write_text(json.dumps({"stage": stage}), encoding="utf-8")
|
|
|
|
def _bash(self, command: str) -> str:
|
|
return json.dumps({"tool_input": {"command": command}})
|
|
|
|
def test_block_only_heavy_jobs_at_stop_and_never_teardown(self):
|
|
self._stage("stop")
|
|
self.assertEqual(self._run("block", stdin=self._bash("make compose-deploy apps=x")).returncode, 2)
|
|
self.assertEqual(self._run("block", stdin=self._bash("make swarm-down")).returncode, 0)
|
|
self.assertEqual(self._run("block", stdin=self._bash("ls")).returncode, 0)
|
|
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()
|
|
self._stage("stop")
|
|
message = "$(cat <<'EOF'\nfix: run \"make test\" and make swarm-zombie\nEOF\n)"
|
|
for command in (
|
|
'git commit --no-verify -m "fix: passes the vars to make swarm-zombie app=x"',
|
|
"grep -n 'make test' Makefile",
|
|
f'git commit --no-verify -m "{message}"',
|
|
):
|
|
self.assertEqual(self._run("block", stdin=self._bash(command)).returncode, 0, command)
|
|
for command in (
|
|
"make -C x swarm-zombie app=y",
|
|
"env FOO=1 make swarm-zombie",
|
|
'make compose-deploy apps="web-app-x"',
|
|
):
|
|
self.assertEqual(self._run("block", stdin=self._bash(command)).returncode, 2, command)
|
|
|
|
def test_admit_keeps_the_stop_floor(self):
|
|
self.cfg["thresholds_gb"] = {"warn": 0, "stop": 0, "critical": 0}
|
|
self._write_config()
|
|
self.assertEqual(self._run("admit", "--need-gb", "0.001").returncode, 0)
|
|
self.assertEqual(self._run("admit", "--need-gb", "1e9").returncode, 3)
|
|
|
|
def test_admit_for_a_load_kind_also_needs_cpu_and_ram_below_the_limits(self):
|
|
self.cfg["thresholds_gb"] = {"warn": 0, "stop": 0, "critical": 0}
|
|
self._write_config()
|
|
self.assertEqual(self._run("admit", "--kind", "test").returncode, 0)
|
|
self.cfg["load_limits"]["cpu_percent"] = 0
|
|
self._write_config()
|
|
denied = self._run("admit", "--kind", "test")
|
|
self.assertEqual(denied.returncode, 3)
|
|
self.assertIn("cpu", denied.stdout)
|
|
self.assertEqual(self._run("admit", "--need-gb", "0.001").returncode, 0)
|
|
|
|
def test_warn_alert_reaches_each_session_once(self):
|
|
self.cfg["thresholds_gb"] = {"warn": 1e9, "stop": -1, "critical": -2}
|
|
self._write_config()
|
|
|
|
def hook(session: str) -> str:
|
|
payload = {"session_id": session, "hook_event_name": "UserPromptSubmit"}
|
|
return self._run("watch", stdin=json.dumps(payload)).stdout
|
|
|
|
self.assertIn("RESOURCE WARN", hook("s1"))
|
|
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)
|
|
new.write_bytes(b"y" * 4096)
|
|
os.utime(old, (1_000_000, 1_000_000))
|
|
os.utime(new, (2_000_000, 2_000_000))
|
|
st = os.statvfs(self.base)
|
|
self.cfg["cache_budget_percent"] = (4096 + 100) / (st.f_blocks * st.f_frsize) * 100
|
|
self._write_config()
|
|
self.assertEqual(self._run("cleanup", "--caches").returncode, 0)
|
|
self.assertFalse(old.exists())
|
|
self.assertTrue(new.exists())
|
|
|
|
def _mark_cleaned(self) -> list[str]:
|
|
return [sys.executable, "-c", "import sys; open(sys.argv[1], 'a').write('x')", str(self.base / "cleaned-{project}")]
|
|
|
|
def test_full_cleanup_reaches_only_marked_running_worktrees_with_a_compose_project(self):
|
|
marked, foreign, odd = self.base / "marked", self.base / "foreign", self.base / "odd"
|
|
for tree in (marked, foreign, odd):
|
|
tree.mkdir()
|
|
for tree in (marked, odd):
|
|
(tree / "Makefile").write_text("", encoding="utf-8")
|
|
self.cfg["worktrees_command"] = (
|
|
f"printf '%s\\t%s\\n' {marked} stack-a {foreign} stack-b {marked} stack-a {odd} 'x;touch y' {odd}"
|
|
)
|
|
self.cfg["worktree_cleanup"] = [self._mark_cleaned()]
|
|
self._write_config()
|
|
self.assertEqual(self._run("cleanup", "--full").returncode, 0)
|
|
self.assertEqual(sorted(p.name for p in self.base.glob("cleaned-*")), ["cleaned-stack-a"])
|
|
self.assertEqual((self.base / "cleaned-stack-a").read_text(encoding="utf-8"), "x")
|
|
|
|
def test_full_cleanup_skips_a_worktree_where_a_busy_process_runs(self):
|
|
idle, gated = self.base / "idle", self.base / "gated"
|
|
for tree in (idle, gated):
|
|
(tree / "sub").mkdir(parents=True)
|
|
(tree / "Makefile").write_text("", encoding="utf-8")
|
|
gate = subprocess.Popen(["sleep", "60"], cwd=gated / "sub")
|
|
self.addCleanup(gate.wait)
|
|
self.addCleanup(gate.kill)
|
|
self.cfg["worktrees_command"] = f"printf '%s\\t%s\\n' {idle} idle {gated} gated"
|
|
self.cfg["worktree_cleanup"] = [self._mark_cleaned()]
|
|
self._write_config()
|
|
self.assertEqual(self._run("cleanup", "--full").returncode, 0)
|
|
self.assertTrue((self.base / "cleaned-idle").exists())
|
|
self.assertFalse((self.base / "cleaned-gated").exists())
|
|
|
|
def test_a_group_writable_council_root_stops_commands_and_silences_hooks(self):
|
|
self.root.mkdir()
|
|
self.root.chmod(0o777)
|
|
result = self._run("status")
|
|
self.assertEqual(result.returncode, 2)
|
|
self.assertIn("cannot write", result.stderr)
|
|
for hook in ("watch", "block"):
|
|
with self.subTest(hook=hook):
|
|
quiet = self._run(hook, stdin=self._bash("make compose-deploy"))
|
|
self.assertEqual(quiet.returncode, 0)
|
|
self.assertIn("council hooks stay off", quiet.stdout)
|
|
self.assertFalse((self.root / "watch.json").exists())
|
|
|
|
def test_a_symlinked_council_root_stops_every_command(self):
|
|
target = self.base / "elsewhere"
|
|
target.mkdir()
|
|
self.root.symlink_to(target)
|
|
self.assertEqual(self._run("status").returncode, 2)
|
|
|
|
def _victims(self) -> tuple[Path, Path]:
|
|
victim = self.base / "victim"
|
|
victim.write_text("keep", encoding="utf-8")
|
|
victims = self.base / "victims"
|
|
victims.mkdir()
|
|
return victim, victims
|
|
|
|
def _warn_hook(self, session: str) -> subprocess.CompletedProcess:
|
|
self.cfg["thresholds_gb"] = {"warn": 1e9, "stop": -1, "critical": -2}
|
|
self._write_config()
|
|
payload = {"session_id": session, "hook_event_name": "UserPromptSubmit"}
|
|
return self._run("watch", stdin=json.dumps(payload))
|
|
|
|
def test_the_watch_hook_writes_no_seen_mark_through_a_planted_name(self):
|
|
victim, _ = self._victims()
|
|
(self.root / "seen").mkdir(parents=True)
|
|
(self.root / "seen" / "s1").symlink_to(victim)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("RESOURCE WARN", result.stdout)
|
|
self.assertEqual(victim.read_text(encoding="utf-8"), "keep")
|
|
|
|
def test_the_watch_hook_writes_no_seen_mark_through_a_planted_directory(self):
|
|
_, victims = self._victims()
|
|
self.root.mkdir()
|
|
(self.root / "seen").symlink_to(victims)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("RESOURCE WARN", result.stdout)
|
|
self.assertEqual(os.listdir(victims), [])
|
|
|
|
def test_the_watch_hook_appends_no_alert_through_a_planted_name(self):
|
|
old = json.dumps({"id": 0, "stage": "ok", "text": "old"}) + "\n"
|
|
victim, _ = self._victims()
|
|
victim.write_text(old, encoding="utf-8")
|
|
self.root.mkdir()
|
|
(self.root / "alerts.jsonl").symlink_to(victim)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual((result.returncode, result.stdout), (0, ""), result.stderr)
|
|
self.assertEqual(victim.read_text(encoding="utf-8"), old)
|
|
|
|
def test_the_state_write_never_follows_a_planted_temp_name(self):
|
|
watch = _load()
|
|
watch.ROOT = self.root
|
|
victim, _ = self._victims()
|
|
self.root.mkdir()
|
|
(self.root / f"watch.json.{os.getpid()}").symlink_to(victim)
|
|
watch.write_json(self.root / "watch.json", {"stage": "ok"})
|
|
self.assertEqual(victim.read_text(encoding="utf-8"), "keep")
|
|
self.assertFalse((self.root / "watch.json").is_symlink())
|
|
self.assertEqual(json.loads((self.root / "watch.json").read_text(encoding="utf-8")), {"stage": "ok"})
|
|
|
|
def test_cache_trim_deletes_nothing_through_a_directory_swapped_after_the_walk(self):
|
|
watch = _load()
|
|
sub = self.cache / "sub"
|
|
sub.mkdir()
|
|
(sub / "f").write_bytes(b"x" * 4096)
|
|
_, victims = self._victims()
|
|
(victims / "f").write_bytes(b"v")
|
|
walk = watch.cache_files
|
|
|
|
def swapped(dirs: list[str]) -> list:
|
|
files = walk(dirs)
|
|
sub.rename(self.cache / "moved")
|
|
sub.symlink_to(victims)
|
|
return files
|
|
|
|
watch.cache_files = swapped
|
|
self.cfg["cache_budget_percent"] = 0
|
|
watch.trim_caches(self.cfg)
|
|
self.assertEqual(os.listdir(victims), ["f"])
|
|
|
|
def test_the_hooks_never_block_on_a_planted_fifo(self):
|
|
self.root.mkdir()
|
|
for name in ("watch.json", "radio-silence.json", "alerts.jsonl"):
|
|
os.mkfifo(self.root / name, 0o600)
|
|
env = {
|
|
"PATH": str(self.base),
|
|
"HOME": str(self.base),
|
|
"AGENTS_COUNCIL_ROOT": str(self.root),
|
|
"AGENTS_COUNCIL_CONFIG": str(self.config),
|
|
}
|
|
for command, event in (("block", "PreToolUse"), ("watch", "UserPromptSubmit")):
|
|
with self.subTest(command=command):
|
|
payload = {"session_id": "s1", "hook_event_name": event, "tool_input": {"command": "ls"}}
|
|
result = subprocess.run(
|
|
[sys.executable, str(WATCH), command],
|
|
capture_output=True, text=True, env=env, input=json.dumps(payload), check=False, timeout=30,
|
|
)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
|
|
def test_the_watch_hook_skips_an_alert_line_that_is_no_alert(self):
|
|
self.root.mkdir()
|
|
(self.root / "alerts.jsonl").write_text(
|
|
"not json\n[1]\n" + json.dumps({"id": "x", "text": 1}) + "\n" + "[" * 200000 + "\n", encoding="utf-8"
|
|
)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertIn("RESOURCE WARN", result.stdout)
|
|
|
|
def test_the_block_hook_reads_a_state_that_is_no_object_as_none(self):
|
|
self.root.mkdir()
|
|
(self.root / "watch.json").write_text("[]", encoding="utf-8")
|
|
(self.root / "radio-silence.json").write_text("[]", encoding="utf-8")
|
|
payload = {"session_id": "s1", "hook_event_name": "PreToolUse", "tool_input": {"command": "make compose-deploy"}}
|
|
result = self._run("block", stdin=json.dumps(payload))
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
|
|
def test_cache_trim_counts_a_hard_linked_file_once(self):
|
|
watch = _load()
|
|
(self.cache / "big").write_bytes(b"x" * 65536)
|
|
for index in range(100):
|
|
os.link(self.cache / "big", self.cache / f"link{index}")
|
|
kept = self.cache / "kept"
|
|
kept.write_bytes(b"x" * 4096)
|
|
os.utime(kept, (0, 0))
|
|
self.cfg["cache_budget_percent"] = 100 * 1e6 / watch.disk(self.cfg)[1] / watch.GB
|
|
watch.trim_caches(self.cfg)
|
|
self.assertTrue(kept.exists())
|
|
|
|
def test_the_watch_hook_reads_no_alert_through_a_planted_name(self):
|
|
outside = self.base / "outside.jsonl"
|
|
outside.write_text(json.dumps({"id": time.time_ns(), "stage": "warn", "text": "OUTSIDE"}) + "\n", encoding="utf-8")
|
|
self.root.mkdir()
|
|
(self.root / "alerts.jsonl").symlink_to(outside)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertNotIn("OUTSIDE", result.stdout)
|
|
|
|
def test_the_watch_hook_keeps_no_history_row_read_through_a_planted_name(self):
|
|
outside = self.base / "outside.jsonl"
|
|
outside.write_text(json.dumps({"id": "outside"}) + "\n", encoding="utf-8")
|
|
self.root.mkdir()
|
|
(self.root / "queue-history.jsonl").symlink_to(outside)
|
|
result = self._warn_hook("s1")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
kept = self.base / ".local" / "state" / "agents-council" / "queue-history.jsonl"
|
|
self.assertNotIn("outside", kept.read_text(encoding="utf-8") if kept.exists() else "")
|
|
|
|
def test_cache_trim_counts_the_blocks_a_sparse_file_holds(self):
|
|
watch = _load()
|
|
with (self.cache / "sparse").open("wb") as handle:
|
|
handle.truncate(10**12)
|
|
kept = self.cache / "kept"
|
|
kept.write_bytes(b"x" * 4096)
|
|
os.utime(kept, (0, 0))
|
|
watch.trim_caches(self.cfg)
|
|
self.assertTrue(kept.exists())
|
|
|
|
def test_cleanup_survives_a_stale_lock_link_and_removes_nothing(self):
|
|
_, victims = self._victims()
|
|
self.root.mkdir()
|
|
lock = self.root / "cleanup.lock"
|
|
lock.symlink_to(victims)
|
|
os.utime(victims, (0, 0))
|
|
os.utime(lock, (0, 0), follow_symlinks=False)
|
|
result = self._run("cleanup", "--caches")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertTrue(victims.is_dir())
|
|
self.assertTrue(lock.is_symlink())
|
|
|
|
def test_without_config_every_command_is_a_no_op(self):
|
|
self.config.unlink()
|
|
result = self._run("status")
|
|
self.assertEqual((result.returncode, result.stdout), (0, ""))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|