Files
synapbus/examples/doc-gardener/run_task.sh
T
Algis DumbrisandClaude Opus 4.6 3b94fab226 feat(018): real reactor-driven multi-agent doc-gardener flow
Until now the doc-gardener example was a single monolithic
orchestrator binary writing synthetic messages directly to SQLite.
That's now obsolete: the feature runs as a true multi-agent flow
where the SynapBus reactor fires subprocess runs for every DM, each
agent is its own reactive subprocess invocation, and follow-up DMs
go through the real MessagingService.Send → dispatcher path so the
reactor picks them up.

Changes:

- cmd/synapbus/main.go: gate the three legacy background workers
  (expiry, retention, stalemate) behind SYNAPBUS_DISABLE_*_WORKER env
  flags. These workers manage the legacy channel task-auction /
  message retention features the doc-gardener demo doesn't use, but
  they held the single-connection write pool long enough to wedge
  the whole server for interactive sessions. All three are disabled
  in the example's start.sh.

- cmd/docgardener/agent.go (new): the per-agent subprocess entry the
  reactor harness invokes for every reactive trigger. Reads
  message.json from the workdir, routes by SYNAPBUS_AGENT to either
  coordinator-kickoff, coordinator-completion, or specialist-work
  logic. Writes prompt.txt + response.txt for harness capture. Uses
  the admin socket (`synapbus messages send`) for follow-up DMs so
  the real MessagingService.Send path fires the dispatcher.

- cmd/docgardener/main.go: adds `docgardener agent` subcommand, plus
  helpers freshAPIKey / bcryptHash / absPath / selfPath used by the
  spawn flow.

- examples/doc-gardener/start.sh: provisions user + coordinator
  agent + algis human agent + approvals/requests channels; the
  coordinator is created with trigger_mode=reactive,
  harness_name=subprocess, local_command pointing to docgardener
  agent, and harness_config_json.env carrying SYNAPBUS_AGENT,
  SYNAPBUS_BIN, SYNAPBUS_SOCKET. Specialists are spawned
  dynamically by the coordinator at runtime (not pre-registered),
  so the demo exercises dynamic agent spawning end-to-end.

- examples/doc-gardener/run_task.sh: collapsed to a 3-line kickoff
  that just DMs the coordinator and polls algis's inbox for the
  coordinator's FINAL: reply. Everything else happens via the
  reactor.

Verified end-to-end in Chrome on a fresh instance:
- 4 agents registered (coordinator + 3 specialists dynamically
  spawned by the coordinator on receipt of the first DM)
- 7 reactive_runs + 6 harness_runs across the goal lifecycle:
    algis → coordinator (kickoff, 624ms, builds goal+tree+spawns)
    coordinator → docs-scanner (claim task 2)
    coordinator → cli-verifier (claim task 3)
    coordinator → drift-reporter (claim task 4)
    docs-scanner → coordinator (DONE task=2)
    cli-verifier → coordinator (DONE task=3)
    drift-reporter → coordinator (DONE task=4, coalesced)
- Web UI Agent Runs page shows all 7 runs with the real
  "DM from X" trigger lines and correct sender/receiver chain
- Goal ends at status=completed with all 3 leaf tasks at status=done
- Each specialist run posts a real subprocess artifact to the
  goal channel (#finding, #verified, #summary) and appends a real
  reputation_evidence row keyed by config_hash.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 17:09:08 +03:00

66 lines
2.3 KiB
Bash
Executable File

#!/bin/bash
# run_task.sh — kick off the real multi-agent doc-gardener demo.
#
# Sends a single DM from algis to doc-gardener-coordinator. The
# reactor picks it up, fires a subprocess running `docgardener agent`,
# which creates the goal, materializes the task tree, spawns the 3
# specialists (dynamically — they didn't exist before this run), and
# DMs each to claim its task. Every specialist fires its own reactor
# run, does real subprocess work, posts artifacts, and DMs the
# coordinator back. The coordinator's completion handler waits until
# all tasks resolve, then DMs algis with FINAL: summary.
#
# Nothing in this script touches the DB directly — the whole demo is
# driven by SynapBus messaging + the reactor.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BIN="$SCRIPT_DIR/bin/synapbus"
SOCKET="$SCRIPT_DIR/data/synapbus.sock"
cd "$SCRIPT_DIR"
say() { printf '\033[1;36m[run]\033[0m %s\n' "$*"; }
die() { printf '\033[1;31m[run][FAIL]\033[0m %s\n' "$*" >&2; exit 1; }
if [ ! -x "$BIN" ]; then
die "synapbus binary not found at $BIN — run ./start.sh first"
fi
if [ ! -S "$SOCKET" ]; then
die "admin socket not found at $SOCKET — is synapbus running?"
fi
GOAL_DESC='Verify every CLI flag and config option mentioned in docs.mcpproxy.app actually exists in the mcpproxy binary, flag any drift, and propose doc patches.'
say "sending kickoff DM: algis → doc-gardener-coordinator"
printf '%s' "$GOAL_DESC" | "$BIN" --socket "$SOCKET" messages send \
--from algis \
--to doc-gardener-coordinator \
--priority 8 >&2
say "waiting for coordinator's FINAL: reply to algis (up to 120s)..."
deadline=$(( $(date +%s) + 120 ))
while [ $(date +%s) -lt $deadline ]; do
FINAL=$("$BIN" --socket "$SOCKET" messages list --to algis --limit 10 2>/dev/null \
| awk '/^FINAL: / {print; exit}')
if [ -n "${FINAL:-}" ]; then
say "coordinator reported: $FINAL"
break
fi
sleep 1
done
# Find the latest goal id for the report step.
GOAL_ID=$(sqlite3 "$SCRIPT_DIR/data/synapbus.db" 'SELECT id FROM goals ORDER BY id DESC LIMIT 1')
if [ -n "$GOAL_ID" ]; then
echo "$GOAL_ID" > "$SCRIPT_DIR/.last_goal_id"
say "goal id = $GOAL_ID"
fi
say "done. render the report with:"
echo " ./report.sh"
echo
say "Agent Runs: http://localhost:18089/runs"
say "Agents: http://localhost:18089/agents"