From 09f68dd75b6956c5617a6a0dd5619b38bd84906e Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Sun, 8 Nov 2026 06:46:03 +0000 Subject: [PATCH] fix(orchestrator): run the tree timer a minute after each run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: a pkgmgr branch tree run takes 72.6 s real (55.5 s user) on this host, while TimeoutStartSec=55 killed every run; the timer fired again each minute, so the service burned a core and branch-tree.json never appeared. - kvb-orchestrator-tree.timer: OnActiveSec=30s plus OnUnitInactiveSec=60s instead of OnCalendar=minutely, so runs never overlap and the next one starts 60 s after the last ended. - kvb-orchestrator-tree.service: TimeoutStartSec=600 and CPUQuota=50%. - orchestrator.sh restarts the timer on install so the new schedule takes effect; README and requirement 001 follow. 🎯 Fix: 92 % πŸ’₯ Break: 4 % πŸ’Έ Cost: 100 % ⏱️ Time: – πŸ›‘οΈ Security: 100 % πŸ† Score: 88 🚦 Gate: βœ… 90/5, cost and time <= 100, security 100 % Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- docs/requirements/001-agent-orchestrator.md | 5 +++-- orchestrator/systemd/kvb-orchestrator-tree.service | 3 ++- orchestrator/systemd/kvb-orchestrator-tree.timer | 5 +++-- scripts/orchestrator.sh | 3 ++- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b613d0b..c6da6e0 100644 --- a/README.md +++ b/README.md @@ -403,7 +403,7 @@ make project TARGET=/path/to/repo Both commands also patch the target's `.claude/settings.local.json` β€” the machine-local, untracked file, so a project's reviewed `.claude/settings.json` is never rewritten by an install: the caveman and ponytail plugins are enabled so their modes auto-activate on session start, and two reminder hooks are registered β€” `kvb-dream` points each agent at `/kvb-dream` at most once every twelve hours (stamped per session in `~/.claude/.dream-stamps/`), and `kvb-coach` runs its short form at the first prompt of every full hour, once across all agents (stamped in `~/.claude/.coach-hours/`); the global install also enables the `kvb-dream-notify.timer` systemd user timer, which recommends `/kvb-dream` in a GNOME notification daily at 17:00 local time and catches up once after a missed run. A PreToolUse gate from the `kvb-org-organize` skill blocks edits to files in a git worktree until the session holds a claim (`skills/kvb-org-organize/scripts/council.py`), and its resource watch (`skills/kvb-org-organize/scripts/watch.py`) measures disk, Docker and caches, cleans up low-risk data and blocks new heavy jobs before the disk runs out; it stays inactive until `~/.claude/agents-council/config.json` exists, and because its hooks run that config's commands outside the sandbox, the install adds `Edit(~/.claude/agents-council/**)` to `permissions.deny`, which also covers Write in every permission mode. The `kvb-org-radio` skill registers five hooks: its prompt hook and a PostToolBatch hook deliver the immediate radio (`call`, `broadcast`, `security`, `pan-pan`, `mayday`) mid-turn, a Stop hook delivers the post (`post`, `newsletter` and call receipts) when the turn ends, an `asyncRewake` Stop hook wakes an idle session for the radio, and a PreToolUse hook refuses every writing action while a mayday holds, except those of its sender and of the agents that join it with `radio.py assist`. The PreToolUse hook of the `kvb-org-queue` skill sends heavy commands through `queue.py hold` once the config sets `queue_enforce`, and holds a held command to the settings' Bash deny and ask rules, which see only `queue.py hold`. The global install also installs the MEDUSA security scanner with pipx for `kvb-check-security`, unless `medusa` is already on the PATH. Two hooks of the `kvb-org-perform` skill (`skills/kvb-org-perform/scripts/log.py`) log every Agent spawn with its task class, model and effort, and store the quality feedback a sandboxed session prints, in `~/.local/state/agents-council/subagent-model.jsonl`. Every hook runs `/usr/bin/python3 -I`, so neither the `python3` of a sandbox-writable virtualenv on the `PATH` nor `PYTHONPATH` reaches the hooks, which run outside the sandbox; older bare `python3` hook entries are rewritten. The reminder hooks run `/usr/bin/bash` and pin `PATH=/usr/bin:/bin`, and the council scripts keep only the `PATH` directories this uid cannot write (`skills/kvb-org-organize/scripts/hookpath.py`), so no binary an agent plants on the inherited `PATH` runs outside the sandbox. Existing settings are preserved. -The global install also runs the Agent Orchestrator, a local review page in which the operator approves or rejects the agents' staged commits (the `kvb-git-diff` skill): it copies `orchestrator/` to `~/.local/share/kvb-orchestrator`, starts it there with `docker compose up -d --build` (restart `unless-stopped`, bound to `127.0.0.1` only, state in `~/.local/state/kvb-orchestrator`) and registers its MCP server for Claude Code at user scope. Its port is `KVB_ORCHESTRATOR_PORT` in `orchestrator/.env`; the hub page is `http://localhost:/`, the MCP endpoint `/mcp`. Without docker the hub is skipped and the commit skills ask in chat. The hub reads new radio traffic aloud in maritime phraseology and takes push-to-talk input; its image builds both engines from pinned, sha256-verified sources and runs each as a separate process: Piper (`piper-tts`, GPL-3.0-or-later) with the `en_US-libritts-high` voice (904 speakers, trained on LibriTTS, CC BY 4.0, from `rhasspy/piper-voices`) and whisper.cpp (MIT) with the multilingual `ggml-base-q8_0` model (OpenAI Whisper weights, MIT, 82 MB). The operator's own voice is `KVB_ORCHESTRATOR_OPERATOR_VOICE` in `orchestrator/.env`. Its Tree reads only the file `pkgmgr branch tree` writes: the install clones `PKGMGR_REPO` (default the GitHub repository, never the sandbox-writable local checkout), builds the newest `v*` tag a fully trusted GPG key signed into `~/.local/share/kvb-orchestrator/pkgmgr`, and enables the `kvb-orchestrator-tree.timer` user timer, which writes `~/.local/state/pkgmgr/tree/branch-tree.json` every minute while the hub page polls; the hub mounts that directory read-only and starts no git. A signed tag without `branch tree` leaves the timer off. +The global install also runs the Agent Orchestrator, a local review page in which the operator approves or rejects the agents' staged commits (the `kvb-git-diff` skill): it copies `orchestrator/` to `~/.local/share/kvb-orchestrator`, starts it there with `docker compose up -d --build` (restart `unless-stopped`, bound to `127.0.0.1` only, state in `~/.local/state/kvb-orchestrator`) and registers its MCP server for Claude Code at user scope. Its port is `KVB_ORCHESTRATOR_PORT` in `orchestrator/.env`; the hub page is `http://localhost:/`, the MCP endpoint `/mcp`. Without docker the hub is skipped and the commit skills ask in chat. The hub reads new radio traffic aloud in maritime phraseology and takes push-to-talk input; its image builds both engines from pinned, sha256-verified sources and runs each as a separate process: Piper (`piper-tts`, GPL-3.0-or-later) with the `en_US-libritts-high` voice (904 speakers, trained on LibriTTS, CC BY 4.0, from `rhasspy/piper-voices`) and whisper.cpp (MIT) with the multilingual `ggml-base-q8_0` model (OpenAI Whisper weights, MIT, 82 MB). The operator's own voice is `KVB_ORCHESTRATOR_OPERATOR_VOICE` in `orchestrator/.env`. Its Tree reads only the file `pkgmgr branch tree` writes: the install clones `PKGMGR_REPO` (default the GitHub repository, never the sandbox-writable local checkout), builds the newest `v*` tag a fully trusted GPG key signed into `~/.local/share/kvb-orchestrator/pkgmgr`, and enables the `kvb-orchestrator-tree.timer` user timer, which writes `~/.local/state/pkgmgr/tree/branch-tree.json` a minute after its previous run while the hub page polls, on at most half a core; the hub mounts that directory read-only and starts no git. A signed tag without `branch tree` leaves the timer off. Stream β†’ Radio (`#stream/radio`) is the hub's radio interface. Its station list shows one line per station, the full name in the tooltip, with its state and a play or stop button; when the browser blocks autoplay, the stream fails or the CSP refuses its origin, the row and a toast say so, and one click starts the station. New stations play daily around the clock. While the hub reads a message aloud, the speaking agent pulses there and in the Orbit and Chord views, under the caption " spricht" with the message's first line; under `prefers-reduced-motion` the ring stays still. The composer sends on all seven channels: post and call to one agent, newsletter, broadcast, pan-pan, security and mayday to all. "Aufnehmen" records with MediaRecorder; the hub transcribes the recording with whisper.cpp off the request thread (at most 120 s of audio, a 120 s timeout, four at a time), keeps it for replay in `~/.local/state/kvb-orchestrator/voice` (the newest 50) and puts the transcript into the field to edit; the message goes out as text, with the recording's host path and sha256 only when "Aufnahme anhΓ€ngen" is ticked. The hub transcribes at most six recordings a minute and 600 s of audio an hour. Every operator message is passkey-signed, and the hub verifies the signature against the pinned keys, each once, before it relays; `operator_message(id, text, to)` answers `match` only for the signed text and recipient; for a denial the signature covers the recipient the inbox resolved at signing, and the answer's text is only the signed reason, never the agent's own words the radio row quotes. "Mayday aufheben", Abend, Sonnenuntergang, Sonnenaufgang and a settings save that adds a stream origin take the passkey the same way. diff --git a/docs/requirements/001-agent-orchestrator.md b/docs/requirements/001-agent-orchestrator.md index 2b05cde..e52efb6 100644 --- a/docs/requirements/001-agent-orchestrator.md +++ b/docs/requirements/001-agent-orchestrator.md @@ -453,8 +453,9 @@ flowchart TD `core.fsmonitor`). - [ ] `pkgmgr branch open|close|drop|gc` rewrite the file when it exists. - [ ] The orchestrator install enables a systemd user timer that writes it - every 60 s while the page polled within the last 5 min, with pkgmgr - built from the newest signed tag. + 60 s after its previous run ended while the page polled within the + last 5 min, on at most half a core, with pkgmgr built from the newest + signed tag. - [ ] The orchestrator mounts the file read-only and reads only it. - [ ] `orchestrator/kvb_orchestrator/tree.py`'s git scan is gone and no module of the orchestrator starts `git`. diff --git a/orchestrator/systemd/kvb-orchestrator-tree.service b/orchestrator/systemd/kvb-orchestrator-tree.service index 597e652..a00696f 100644 --- a/orchestrator/systemd/kvb-orchestrator-tree.service +++ b/orchestrator/systemd/kvb-orchestrator-tree.service @@ -8,4 +8,5 @@ ExecCondition=/bin/sh -c 'test -n "$(find %h/.local/state/kvb-orchestrator/tree- ExecStart=%h/.local/share/kvb-orchestrator/pkgmgr/bin/pkgmgr branch tree --format json --output %h/.local/state/pkgmgr/tree/branch-tree.json Nice=10 IOSchedulingClass=idle -TimeoutStartSec=55 +CPUQuota=50% +TimeoutStartSec=600 diff --git a/orchestrator/systemd/kvb-orchestrator-tree.timer b/orchestrator/systemd/kvb-orchestrator-tree.timer index 2cec3c0..1f74d07 100644 --- a/orchestrator/systemd/kvb-orchestrator-tree.timer +++ b/orchestrator/systemd/kvb-orchestrator-tree.timer @@ -1,8 +1,9 @@ [Unit] -Description=Agent Orchestrator tree every 60 s +Description=Agent Orchestrator tree a minute after its last run [Timer] -OnCalendar=minutely +OnActiveSec=30s +OnUnitInactiveSec=60s AccuracySec=1s [Install] diff --git a/scripts/orchestrator.sh b/scripts/orchestrator.sh index 119a8ea..c2f0215 100644 --- a/scripts/orchestrator.sh +++ b/scripts/orchestrator.sh @@ -225,7 +225,8 @@ tree_timer() { cp -f "${HUB}/systemd/${TREE_UNIT}.service" "${HUB}/systemd/${TREE_UNIT}.timer" "${HUB}/systemd/${CLEANUP_UNIT}.service" "${HUB}/systemd/${CLEANUP_UNIT}.path" "${UNITS}/" systemctl --user daemon-reload systemctl --user enable --now "${TREE_UNIT}.timer" "${CLEANUP_UNIT}.path" - log "skills: ${TREE_UNIT}.timer writes ${TREE_DIR}/branch-tree.json every 60 s while the Agent Orchestrator page polls" + systemctl --user restart "${TREE_UNIT}.timer" + log "skills: ${TREE_UNIT}.timer writes ${TREE_DIR}/branch-tree.json a minute after each run while the Agent Orchestrator page polls" log "skills: ${CLEANUP_UNIT}.path deletes the merged branches the operator signs in the Artifacts Tree, locally only" }