Files
skills/skills/kvb-org-queue/SKILL.md
T
Kevin Veen-BirkenbachandClaude Opus 5.5 3843821674 fix(kvb-org-organize): harden the council hooks against sandboxed agents
- Move the council config to ~/.claude/agents-council/config.json, which the sandbox cannot write; the installer adds Edit(~/.claude/agents-council/**) to permissions.deny.
- Run every kvb hook as /usr/bin/python3 -I and rewrite old bare python3 entries.
- Refuse a council root that is a symlink, another uid's, or group or other writable, in watch, queue and radio.
- Run worktree_cleanup entries as argv without a shell in /, only for compose project names.
- queue block: tokenize every Bash command like bash (quotes, word-start comments, line continuations), and hold each held command to the Bash deny and ask rules of the managed, user and project settings; refuse a hold it cannot parse.
- radio: deliver fields on one line of at most 1000 characters, at most 10 rows.

Operator step after install: move ~/.config/agents-council/config.json to ~/.claude/agents-council/config.json.

MEDUSA acceptances (kvb-think-criticize 2026-11-06, skeptics A and B): ACP-SCAN-028 load_config (config outside the sandbox, Docker residual 093); CODEGEN-SCAN-004 and LLM06 run_cleanup and worktrees_command shell strings (operator config only); SAST-PATH-001 queue hold Popen (the caller's own command in its sandbox); PA-133 radio hook context (peer rows, clipped); JB-SCAN-641/616/2979 clip and MM-SCAN-058 replace (name matches).

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 13:26:59 +00:00

3.6 KiB

name, description
name description
kvb-org-queue Run every heavy job (gate, deploy, build, swarm) through a deterministic queue instead of asking the chair: hotfix first, then register rank.

${CLAUDE_SKILL_DIR}/scripts/queue.py replaces asking the chair for heavy slots with one queue that every waiting agent evaluates by the same rules.

Run a heavy job

queue.py hold --kind <kind> --task <register task> -- <command>

Start it in the background (run_in_background or equivalent) and end the turn: hold waits, runs the command on its grant and exits with its code.

  • <kind>: a key of slot_need_gb in ~/.claude/agents-council/config.json.
  • <register task>: your open kvb-org-rasci task; its rank places you.
  • --hotfix <slug>: the job proves the register row hotfix/<slug> (kvb-git-hotfix) and goes first.
  • --need-gb <N> overrides the learned disk need.
  • Start hold in the job's worktree: its git toplevel is the workdir. Other runtimes add --session <id>.
  • In a worktree the command stays gate.py run|hold (kvb-check-gate): queue.py hold ... -- python3 <gate.py> run ....

Light runs (no Docker, about a minute) self-admit with watch.py admit --kind test-nostack (kvb-org-organize).

Order and grant

Every 20 s each waiting hold evaluates the whole queue under one lock:

  1. Hotfix jobs first, then the register rank (rasci.py show), one place earlier per whole hour waited, then arrival.
  2. In that order, a job is blocked at disk stage stop or critical, at warn when it needs disk, during a radio-silence mayday, while its kind's slots are taken, or while its workdir runs another job.
  3. A job whose need would cut below the stop threshold reserves that disk against smaller jobs behind it.
  4. An unblocked job counts as granted for the jobs behind it: none takes its slot.
  5. Kinds in load_limits.kinds also need CPU and RAM below the limits.

A waiting hold prints a stderr line when its position or reason changes.

History and learning

Every finished job appends a row to the council's queue-history.jsonl: signature (make <target>, <script>.py <subcommand>), kind, minutes, exit code, and disk, RAM and CPU at the grant and at their 20 s extremes. The watch hook of kvb-org-organize keeps them in ~/.local/state/agents-council/ across reboots.

From the last 30 days, once 5 runs exist:

  • per signature, the disk need is p90 disk taken plus 1 GB;
  • per kind, the slot limit is how many p90 RAM and CPU shares fit between the median load at the grant and load_limits, at least 1.

Until then the seeds hold: slot_need_gb[kind] for disk, 1 slot per kind. The optional slot_limits {kind: N} only caps a limit from above.

Inspect and cancel

queue.py list
queue.py history
queue.py cancel <id>

list shows each job's position, need, wait, ETA and blocking reason; dead holders drop out. The ETA adds the median minutes left of running jobs and of the jobs ahead, - while one is unknown. history prints the learned values per signature and per kind, with each limit's source. cancel removes your own waiting job; its hold exits 3.

Rules

  • Never ask the chair for a heavy slot or announce a grant; the chair handles only exceptions such as a stuck queue or a hung job (capacity.md of kvb-org-organize).
  • A running job is never cut short for the queue; only the operator orders one down, and kvb-git-hotfix may void a gate.
  • With queue_enforce: true, the block hook (PreToolUse on Bash) refuses heavy_commands and gate.py run|hold outside queue.py hold.
  • Without the config, hold exits 2: tell the operator and ask the chair (kvb-org-organize) until it exists.