Files
skills/orchestrator/client/offices.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 4540494c30 feat(orchestrator): hierarchy view from council offices with a separate agent order
The Agents views gain a Hierarchie view: an org chart of the chair and
the line offices read from the council claims. The operator appoints,
hands over and recalls an office there; the page signs each change with
the passkey and a host unit (client/offices.py, offices path unit)
verifies the signature, removes a live holder itself and runs a plain
council.py claim for the successor. council.py loses the --operator
flag, so no agent can override a live holder.

The agent order is now separate from task priority: rasci.py agents
order stores it in agent-order.json beside the register through the
nofollow helpers and never writes a --prio pin. It only breaks ties of
equal task rank, also among the queue grants. The Hierarchie view edits
it as a drag list.

Host units run the installed skill scripts, so orchestrator.sh keeps the
offices unit off while the installed council.py, nofollow.py or rasci.py
differ from the tree. Job files, council files and hub records are read
with O_NOFOLLOW and O_NONBLOCK and size caps; nofollow.rewrite replaces
a claim without creating directories, so a beat never revives a released
office.

🎯 Fix: 92 %
💥 Break: 4 %
💸 Cost: 100 %
⏱️ Time: 79 %
🛡️ Security: 100 %
🏆 Score: 100
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

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

310 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
"""Args:
--port: port of the Agent Orchestrator; the signature's origin is
http://localhost:<port>.
--jobs: the directory the hub writes signed office orders into, e.g.
~/.local/state/kvb-orchestrator/offices/jobs.
--results: the directory each job's result goes to as <id>.json.
--approvers: the pinned passkeys, e.g. ~/.config/kvb-orchestrator/approvers.json.
--replay: this runner's replay store, outside the hub's reach, e.g.
~/.local/state/kvb-orchestrator-approvals/offices.json.
--council: the council directory every agent shares, e.g.
/tmp/agents-council.
--script: the installed council.py of the kvb-org-organize skill.
Returns:
Runs once over every job in --jobs, as the operator on the host: per item
it verifies the operator's passkey signature over action, office, holder,
session and work, refuses a council directory others can write and an
office path holding a link, and checks that the office still has the
signed holder. A takeover or recall then moves the office aside, reads its
claim there and removes it only while that claim names the signed holder,
else it puts the office back; an appoint or takeover ends in a plain
council.py claim for the successor, run as an argument list, never through
a shell: a live Claude Code successor goes in as CLAUDE_CODE_SESSION_ID,
any other as --session. Writes each item's result and removes the job.
The exit code is 0.
"""
from __future__ import annotations
import argparse
import errno
import json
import os
import re
import shutil
import stat
import subprocess
import sys
import time
from pathlib import Path
import signing
import verifier
JOB = re.compile(r"[0-9a-f]{16}\.json")
SAFE_PATH = "/usr/local/bin:/usr/bin:/bin"
CLAIM = "claim.json"
CLAIM_SHAPE = (("session", str), ("runtime", str), ("beat", (int, float)))
DIRECTORY = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC
FILE = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK | os.O_CLOEXEC
READ_LIMIT = 1 << 20
OUTPUT_LIMIT = 4000
TIMEOUT = 120
def read_regular(name: str | Path, dir_fd: int | None) -> bytes:
"""Args:
name: the file, relative to dir_fd or a path; a link, FIFO or other
non-regular file raises OSError, so does one over READ_LIMIT bytes.
dir_fd: the open directory name is relative to, or None.
Returns:
The file's bytes.
"""
fd = os.open(name, FILE, dir_fd=dir_fd)
with open(fd, "rb") as handle:
if not stat.S_ISREG(os.fstat(fd).st_mode):
raise OSError(errno.EINVAL, "not a regular file", str(name))
data = handle.read(READ_LIMIT + 1)
if len(data) > READ_LIMIT:
raise OSError(errno.EFBIG, f"larger than {READ_LIMIT} bytes", str(name))
return data
def claim_in(office_fd: int) -> dict | None:
"""Returns:
The claim of the open office directory in the shape council.py
reads, or None.
"""
try:
claim = json.loads(read_regular(CLAIM, office_fd))
except (OSError, ValueError, RecursionError):
return None
shaped = isinstance(claim, dict) and all(isinstance(claim.get(key), kind) for key, kind in CLAIM_SHAPE)
return claim if shaped else None
def parent_of(council: Path, office: str) -> tuple[int, str]:
"""Returns:
The open directory holding the office, without following a link on
the way, and the office's last name; the caller closes it.
"""
*dirs, name = office.split("/")
fd = os.open(council, DIRECTORY)
try:
for part in dirs:
child = os.open(part, DIRECTORY, dir_fd=fd)
os.close(fd)
fd = child
except BaseException:
os.close(fd)
raise
return fd, name
def unsafe(council: Path, office: str) -> str | None:
"""Returns:
Why the council directory or the office's path below it must not be
used, or None.
"""
try:
root = council.lstat()
except OSError as error:
return f"the council directory is unreadable: {error.strerror}"
if not stat.S_ISDIR(root.st_mode) or root.st_uid != os.getuid() or root.st_mode & 0o022:
return f"{council} is no directory of uid {os.getuid()}, or others can write it"
path = council
for part in office.split("/"):
path = path / part
try:
found = path.lstat()
except FileNotFoundError:
return None
if not stat.S_ISDIR(found.st_mode):
return f"{path} is a link or no directory"
try:
found = (path / CLAIM).lstat()
except FileNotFoundError:
return None
return None if stat.S_ISREG(found.st_mode) else f"{path / CLAIM} is a link or no file"
def holder_of(council: Path, office: str) -> str | None:
"""Returns:
The session holding the office, empty while it is vacant, None while
its claim is missing, unreadable or not in the shape council.py reads.
"""
try:
parent, name = parent_of(council, office)
except FileNotFoundError:
return ""
except OSError:
return None
try:
fd = os.open(name, DIRECTORY, dir_fd=parent)
except FileNotFoundError:
return ""
except OSError:
return None
finally:
os.close(parent)
try:
claim = claim_in(fd)
finally:
os.close(fd)
return claim["session"] if claim else None
def vacate(council: Path, office: str, holder: str) -> str | None:
"""Args:
council: the council directory.
office: the office to empty.
holder: the session the operator signed as its holder.
Returns:
None once the office is gone, else why nothing changed.
"""
parent, name = parent_of(council, office)
try:
tomb = f"{name}.recalled-{os.getpid()}-{time.time_ns()}"
try:
os.rename(name, tomb, src_dir_fd=parent, dst_dir_fd=parent)
except FileNotFoundError:
return f"the holder changed since the signature: signed {holder}, now vacant; nothing changed"
try:
fd = os.open(tomb, DIRECTORY, dir_fd=parent)
except OSError:
claim = None
else:
try:
claim = claim_in(fd)
finally:
os.close(fd)
if claim is not None and claim["session"] == holder:
shutil.rmtree(tomb, dir_fd=parent)
return None
now_held = "unreadable" if claim is None else claim["session"]
try:
os.lstat(name, dir_fd=parent)
except FileNotFoundError:
# Python has no RENAME_NOREPLACE: rename(2) replaces an empty office a claimant made after the lstat.
os.rename(tomb, name, src_dir_fd=parent, dst_dir_fd=parent)
return f"the holder changed since the signature: signed {holder}, now {now_held}; nothing changed"
kept = council.joinpath(*office.split("/")[:-1], tomb)
return f"the holder changed since the signature: signed {holder}, now {now_held}; a new claim arrived meanwhile, the old office waits at {kept}"
finally:
os.close(parent)
def live(home: Path, session: str) -> bool:
for path in (home / ".claude" / "sessions").glob("*.json"):
try:
data = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError):
continue
if isinstance(data, dict) and data.get("sessionId") == session:
return True
return False
class Offices:
def __init__(self, trust: verifier.Trust, council: Path, script: Path, home: Path) -> None:
self.trust = trust
self.council = council
self.script = script
self.home = home
def command(self, item: dict) -> tuple[list[str], dict[str, str]]:
argv = [sys.executable, "-I", "-B", str(self.script), "claim", item["office"], f"--work={item['work']}"]
env = {"PATH": SAFE_PATH, "HOME": str(self.home), "AGENTS_COUNCIL_ROOT": str(self.council)}
if live(self.home, item["session"]):
return argv, {**env, "CLAUDE_CODE_SESSION_ID": item["session"]}
return [*argv, f"--session={item['session']}"], env
def item(self, item: dict, proof: object, now: float) -> dict:
"""Args:
item: one well-formed office order.
proof: the job's {"payload", "assertion"}.
now: the current time in epoch seconds.
Returns:
The order with status done, failed or refused, the reason, the
exit code and the output of council.py; a refused order changed
nothing.
"""
def ended(status: str, reason: str, code: int | None, output: str) -> dict:
return {**item, "status": status, "reason": reason, "code": code, "output": output}
try:
verifier.check(proof, signing.office_item(**item), "", self.trust, now)
except (signing.Refused, OSError) as error:
return ended("refused", f"no valid operator signature: {error}", None, "")
reason = unsafe(self.council, item["office"])
if reason:
return ended("refused", reason, None, "")
current = holder_of(self.council, item["office"])
if current != item["holder"]:
now_held = "unreadable" if current is None else current or "vacant"
return ended("refused", f"the holder changed since the signature: signed {item['holder'] or 'vacant'}, now {now_held}", None, "")
if item["action"] != "appoint":
try:
reason = vacate(self.council, item["office"], item["holder"])
except OSError as error:
return ended("failed", f"the office could not be emptied: {error.strerror or error}; nothing changed", None, "")
if reason:
return ended("refused", reason, None, "")
if item["action"] == "recall":
return ended("done", "", None, f"recalled {item['office']} from {item['holder']}")
argv, env = self.command(item)
try:
done = subprocess.run(argv, cwd="/", env=env, stdin=subprocess.DEVNULL, capture_output=True, text=True, errors="replace", timeout=TIMEOUT, check=False)
except (OSError, subprocess.TimeoutExpired) as error:
return ended("failed", f"{error}; the office is vacant now", 127, "")
output = (done.stdout + done.stderr)[-OUTPUT_LIMIT:]
if done.returncode == 0:
return ended("done", "", 0, output)
vacant = "; the office is vacant now" if item["action"] == "takeover" else ""
return ended("failed", f"{output.strip()[-500:]}{vacant}", done.returncode, output)
def job(self, path: Path, now: float) -> dict:
try:
job = json.loads(read_regular(path, None))
except (OSError, ValueError, RecursionError) as error:
return {"id": path.stem, "finished": now, "items": [], "error": f"unreadable job: {error}"}
items = job.get("items") if isinstance(job, dict) else None
if not isinstance(items, list) or not items or any(signing.office_problem(item) for item in items) or job.get("id") != path.stem:
return {"id": path.stem, "finished": now, "items": [], "error": "malformed job: refused, nothing changed"}
results = [self.item(item, job.get("proof"), now) for item in items]
return {"id": path.stem, "finished": time.time(), "items": results}
def cycle(jobs: Path, results: Path, offices: Offices) -> int:
handled = 0
for path in sorted(jobs.iterdir()):
if JOB.fullmatch(path.name):
verifier.write_private(results / path.name, offices.job(path, time.time()))
handled += 1
if path.is_dir() and not path.is_symlink():
shutil.rmtree(path)
else:
path.unlink(missing_ok=True)
return handled
def main() -> int:
parser = argparse.ArgumentParser(prog="offices")
parser.add_argument("--port", type=int, required=True)
for name in ("--jobs", "--results", "--approvers", "--replay", "--council", "--script"):
parser.add_argument(name, type=Path, required=True)
args = parser.parse_args()
trust = verifier.Trust(approvers=args.approvers, replay=args.replay, origin=signing.origin_of(args.port))
args.results.mkdir(mode=0o700, parents=True, exist_ok=True)
cycle(args.jobs, args.results, Offices(trust, args.council, args.script, Path.home()))
return 0
if __name__ == "__main__":
raise SystemExit(main())