Implement tooling for the shared Emacs daemon control plane on Bezalel. Agents can now poll dispatch.org for tasks, claim work, and report results programmatically. Files: - scripts/emacs-fleet-bridge.py — Python client with 6 commands: poll (find PENDING tasks), claim (PENDING→IN_PROGRESS), done (mark complete), append (status messages), status (health check), eval (arbitrary Elisp). SSH-based communication with Bezalel Emacs daemon. - scripts/emacs-fleet-poll.sh — Shell poll script for crontab integration. Shows connectivity, task counts, my pending/active tasks, recent activity. - skills/autonomous-ai-agents/emacs-control-plane/SKILL.md — Full skill docs covering infrastructure, API, agent loop integration, state machine, and pitfalls. Infrastructure: - Host: Bezalel (159.203.146.185) - Socket: /root/.emacs.d/server/bezalel - Dispatch: /srv/fleet/workspace/dispatch.org - Configurable via BEZALEL_HOST, BEZALEL_SSH_KEY, EMACS_SOCKET env vars Closes #590
5.2 KiB
name, description, version, author, license, metadata
| name | description | version | author | license | metadata | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| emacs-control-plane | Sovereign Control Plane via shared Emacs daemon on Bezalel. Poll dispatch.org for tasks, claim work, report results. Real-time fleet coordination hub. | 1.0.0 | Timmy Time | MIT |
|
Emacs Sovereign Control Plane
Overview
A shared Emacs daemon running on Bezalel acts as a real-time, programmable whiteboard and task queue for the entire AI fleet. Unlike Gitea (async, request-based), this provides real-time synchronization and shared executable notebooks.
Infrastructure
| Component | Value |
|---|---|
| Daemon Host | Bezalel (159.203.146.185) |
| SSH User | root |
| Socket Path | /root/.emacs.d/server/bezalel |
| Dispatch File | /srv/fleet/workspace/dispatch.org |
| Fast Wrapper | /usr/local/bin/fleet-append "message" |
Files
scripts/emacs-fleet-bridge.py # Python client (poll, claim, done, append, status, eval)
scripts/emacs-fleet-poll.sh # Shell poll script for crontab/agent loops
When to Use
- Coordinating multi-agent tasks across the fleet
- Real-time status updates visible to Alexander (via timmy-emacs tmux)
- Shared executable notebooks (Org-babel)
- Polling for work assigned to your agent identity
Do NOT use when:
- Simple one-off tasks (just do them)
- Tasks already tracked in Gitea issues (no duplication)
- Emacs daemon is down (fall back to Gitea)
Quick Start
Poll for my tasks
python3 scripts/emacs-fleet-bridge.py poll --agent timmy
Claim a task
python3 scripts/emacs-fleet-bridge.py claim TASK-001 --agent timmy
Report completion
python3 scripts/emacs-fleet-bridge.py done TASK-001 --result "Merged PR #456" --agent timmy
Append a status message
python3 scripts/emacs-fleet-bridge.py append "Deployed v2.3 to staging" --agent timmy
Check control plane health
python3 scripts/emacs-fleet-bridge.py status
Direct Emacs Lisp evaluation
python3 scripts/emacs-fleet-bridge.py eval "(org-element-parse-buffer)"
Shell poll (for crontab)
bash scripts/emacs-fleet-poll.sh timmy
SSH Access from Other VPSes
Agents on Ezra, Allegro, etc. can interact via SSH:
ssh root@bezalel 'emacsclient -s /root/.emacs.d/server/bezalel -e "(your-elisp-here)"'
Or use the fast wrapper:
ssh root@bezalel '/usr/local/bin/fleet-append "Your message here"'
Configuration
Set env vars to override defaults:
| Variable | Default | Description |
|---|---|---|
BEZALEL_HOST |
159.203.146.185 |
Bezalel VPS IP |
BEZALEL_USER |
root |
SSH user |
BEZALEL_SSH_KEY |
(none) | SSH key path |
BEZALEL_SSH_TIMEOUT |
15 |
SSH timeout in seconds |
EMACS_SOCKET |
/root/.emacs.d/server/bezalel |
Emacs daemon socket |
DISPATCH_FILE |
/srv/fleet/workspace/dispatch.org |
Dispatch org file path |
Agent Loop Integration
In your agent's operational loop, add a dispatch check:
# In heartbeat or cron job:
import subprocess
result = subprocess.run(
["python3", "scripts/emacs-fleet-bridge.py", "poll", "--agent", "timmy"],
capture_output=True, text=True, timeout=30
)
if "→" in result.stdout:
# Tasks found — process them
for line in result.stdout.splitlines():
if "→" in line:
task = line.split("→", 1)[1].strip()
# Process task...
Crontab Setup
# Poll dispatch.org every 10 minutes
*/10 * * * * /path/to/scripts/emacs-fleet-poll.sh timmy >> ~/.hermes/logs/fleet-poll.log 2>&1
Dispatch.org Format
Tasks in the dispatch file follow Org mode conventions:
* PENDING Deploy auth service :timmy:allegro:
DEADLINE: <2026-04-15>
Deploy the new auth service to staging cluster.
* IN_PROGRESS Fix payment webhook :timmy:
Investigating 502 errors on /webhook/payments.
* DONE Migrate database schema :ezra:
Schema v3 applied to all shards.
Agent tags (:timmy:, :allegro:, etc.) determine assignment.
State Machine
PENDING → IN_PROGRESS → DONE
↓ ↓
(skip) (fail/retry)
- PENDING: Available for claiming
- IN_PROGRESS: Claimed by an agent, being worked on
- DONE: Completed with optional result note
Pitfalls
-
SSH connectivity — Bezalel may be unreachable. Always check status before claiming tasks. If down, fall back to Gitea-only coordination.
-
Race conditions — Multiple agents could try to claim the same task. The emacsclient eval is atomic within a single call, but claim-then-read is not. Use the claim function (which does both in one elisp call).
-
Socket path — The socket at
/root/.emacs.d/server/bezalelonly exists when the daemon is running. If the daemon restarts, the socket is recreated. -
SSH key — Set
BEZALEL_SSH_KEYenv var if your agent's default SSH key doesn't match. -
Don't duplicate Gitea — If a task is already tracked in a Gitea issue, use that for progress. dispatch.org is for fleet-level coordination, not individual task tracking.