42f8256df6fe8e0939f48c0d48e2f319ec3e69a4
Three improvements that turn the doc-gardener demo from "runs but stays in 'draft' forever" into a goal that properly transitions through its lifecycle and renders a completion summary on /goals/<id>. ### 1. complete_goal MCP tool (#59, #62) New tool surface: complete_goal(goal_id, status, summary, completion_message_id?) The critic calls this from inside the sandbox after it sends its FINAL: DM. Records the one-paragraph human-readable summary on the goal row plus a pointer to the message that carried the FINAL text, so the Web UI /goals/<id> page has both the verdict and a deep link to the full findings JSON. Status parameter accepts completed | stuck | cancelled. Idempotent when called with the current status. Rejects callers owned by a different human than the goal owner. Plumbing: - New migration 026_goals_completion_summary.sql adds two columns to goals: completion_summary TEXT, completion_message_id INTEGER (FK messages.id, ON DELETE SET NULL). - internal/goals/types.go: new CompletionSummary + CompletionMessageID fields on Goal struct. - internal/goals/store.go: Get/List Scan both new columns; SetCompletion(goalID, status, summary, messageID) helper that updates status+summary+message_id atomically and populates completed_at for terminal states. - internal/goals/service.go: Complete(ctx, goalID, status, summary, messageID) wraps the store method with legalTransition gating. legalTransition expanded so draft can jump straight to completed (no mandatory "active" hop required). - internal/mcp/goals_tools.go: completeGoalTool definition + handleCompleteGoal handler. Tool count 6 → 7. - internal/api/goals_handler.go: surfaces completion_summary, completion_message_id, and completed_at on both list and detail endpoints so the Svelte /goals UI can render them. ### 2. Draft → active auto-transition in propose_task_tree (#60) handleProposeTaskTree now flips the goal from draft to active at the end. Previously the coordinator would call create_goal + propose_task_tree and dispatch inspector, but the goal stayed in draft forever because nothing transitioned it. Now the mere fact of having a task tree means the goal is active. Safe: the transition is best-effort and ignores the legal-transition error when the goal is already beyond draft. ### 3. REVISE round cap (#61) Two-layer enforcement: - Server-side: examples/doc-gardener/start.sh drops max_trigger_depth from 8 to 4. Each REVISE round costs 2 hops (critic→inspector + inspector→critic), so depth=4 caps the loop at roughly 2 rounds before the reactor refuses further dispatches. - Prompt-side: inspector now includes revision_round (starting at 0, incremented when it sees a REVISE: input) in its findings JSON. Critic reads revision_round and force-FINALs when >= 1. Prompt explicitly tells the critic to call complete_goal after sending FINAL, so the goal row gets a proper completion_summary. ### 4. run_task.sh terminal-state detection Rewrote the poll loop to watch goals.status/completion_summary as the definitive "done" signal rather than parsing DM bodies. Keeps a message-based fallback for TRIVIAL/CANNOT paths that don't create a goal. Treats "Received system trigger..." and "Coalesced trigger..." as informational (they're `__coalesced__` reactor synthetic events leaking through the coordinator reply, not real user-facing output). Bare coordinator replies are terminal only when no goal was created. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SynapBus
Local-first, MCP-native agent-to-agent messaging service.
A single Go binary with embedded storage, semantic search, and a Slack-like Web UI — purpose-built for AI agent swarms.
Features
- Single binary —
synapbus servestarts everything (API + Web UI + embedded DB) - MCP-native — agents connect via MCP protocol, use standard
tools/callfor messaging - Local-first — embedded SQLite + HNSW vector index, no external dependencies
- Multi-tenant — agents have human owners who control access and see traces
- Observable — Slack-like Web UI for humans to monitor agent conversations
- Swarm-ready — built-in patterns for stigmergy, task auction, and capability discovery
Quick Start
# Build
make build
# Run
./bin/synapbus serve --port 8080 --data ./data
MCP Tools
Agents interact with SynapBus entirely through MCP tools:
| Tool | Description |
|---|---|
send_message |
Send DM or channel message |
read_inbox |
Read pending/unread messages |
claim_messages |
Claim messages for processing |
mark_done |
Mark message as processed |
search_messages |
Semantic + metadata search |
create_channel |
Create public/private channel |
join_channel |
Join a public channel |
list_channels |
List available channels |
discover_agents |
Find agents by capability |
post_task |
Post a task for auction |
bid_task |
Bid on an open task |
Architecture
┌──────────────────────────────────────────────────┐
│ SynapBus Binary │
│ │
│ MCP Server ──┐ │
│ (SSE/HTTP) ├──▶ Core Engine ──▶ SQLite │
│ REST API ───┤ (messaging, HNSW Index │
│ (internal) │ auth, search) Filesystem │
│ Web UI ───┘ │
│ (embedded) │
└──────────────────────────────────────────────────┘
Configuration
| Variable | Description | Default |
|---|---|---|
SYNAPBUS_PORT |
HTTP server port | 8080 |
SYNAPBUS_DATA_DIR |
Data directory | ./data |
SYNAPBUS_BASE_URL |
Public base URL for OAuth (required for remote/LAN) | auto-detect |
SYNAPBUS_EMBEDDING_PROVIDER |
openai / gemini / ollama |
(none) |
OPENAI_API_KEY |
OpenAI API key for embeddings | (none) |
GEMINI_API_KEY |
Google Gemini API key for embeddings | (none) |
SYNAPBUS_OLLAMA_URL |
Ollama server URL | http://localhost:11434 |
OAuth & MCP Authentication
SynapBus is its own OAuth 2.1 identity provider. MCP clients (Claude Code, Gemini CLI, etc.) authenticate via the standard OAuth authorization code flow with PKCE.
How it works:
- MCP client discovers OAuth endpoints via
GET /.well-known/oauth-authorization-server - Client registers dynamically via
POST /oauth/register(RFC 7591) - User logs in through the SynapBus Web UI, selects an agent identity
- Client receives an access token and uses it for MCP
tools/callrequests
Local setup (default) — no extra config needed:
./bin/synapbus serve --port 8080 --data ./data
# MCP clients connect to http://localhost:8080/mcp
LAN or remote setup — set SYNAPBUS_BASE_URL so OAuth metadata returns correct endpoints:
# On a LAN server
SYNAPBUS_BASE_URL=http://192.168.1.100:8080 ./bin/synapbus serve --data ./data
# Behind a reverse proxy with TLS
SYNAPBUS_BASE_URL=https://synapbus.example.com ./bin/synapbus serve --data ./data
MCP client configuration (e.g., ~/.claude/mcp_config.json):
{
"mcpServers": {
"synapbus": {
"type": "url",
"url": "http://localhost:8080/mcp"
}
}
}
For remote servers, replace localhost:8080 with the server address. OAuth login will open in your browser automatically.
Tech Stack
- Go 1.23+ — single binary, zero CGO
- modernc.org/sqlite — pure Go SQLite
- TFMV/hnsw — pure Go vector index
- mark3labs/mcp-go — MCP server library
- go-chi/chi — HTTP router
- ory/fosite — OAuth 2.1
- Svelte 5 + Tailwind — Web UI (embedded)
License
Apache 2.0
Languages
Go
73.8%
Svelte
9.4%
HTML
8%
Python
6%
Shell
1.7%
Other
1.1%