Ships the MVP slice of spec 018 (dynamic agent spawning):
- 5 new SQLite migrations (021-025): goals + goal_tasks + agent_proposals
+ reputation_evidence + secrets + harness_runs.task_id. The legacy
`tasks` table (channel auctions) and `agent_trust` table (reactions
workflow) are left untouched — the new schema coexists.
- 4 new internal packages, fully tested:
- internal/goals: Goal struct + store + service, slug collision dedup,
backing-channel auto-create via ChannelCreator adapter
- internal/goaltasks: goal_tasks table with denormalized 16 KB
ancestry snapshots, single-statement optimistic-lock atomic claim,
recursive-CTE cost rollup, state machine, per-billing-code rollup
- internal/secrets: NaCl-secretbox encrypted blobs, user/agent/task
scope precedence, sanitized env injection, master-key bootstrap
- internal/trust additions: ConfigHash (deterministic SHA-256 of
model + prompt + tools + skills + mcp + subagents, sorted),
DelegationCap (tier + tool-scope + budget + depth enforcement),
append-only Ledger with exponential time-decay rolling score and
70%-of-parent child seeding. Existing trust package unchanged.
- Critical invariants under test:
- 50-goroutine concurrent claim race → exactly one winner per round
- ConfigHash stable under shuffled array inputs, sensitive to
capability changes
- DelegationCap full tier × tool-scope matrix
- Ledger time-decay + parent seed at 70 % ± 1 %
- Secret name sanitization, scope precedence, plaintext never
returned via MCP-equivalent paths
- internal/agents/types.go extended with dynamic-spawning columns
(config_hash, parent_agent_id, spawn_depth, system_prompt,
autonomy_tier, tool_scope_json, quarantined_at). Existing tests
still pass.
- cmd/docgardener: self-contained demo binary driving the end-to-end
flow. `docgardener run` creates a goal, builds a task tree with
denormalized ancestry, spawns 3 specialists (each going through
real delegation-cap validation and config-hash computation and
70 %-of-parent reputation seeding), claims tasks atomically, runs
them through the state machine, records reputation evidence.
`docgardener report` queries all of that back out and renders a
rich dark-mode HTML report (header, spend metrics, task tree,
spawned-agent cards with reputation bars, cost breakdown, artifacts,
timeline).
- examples/doc-gardener: start.sh / run_task.sh / report.sh / stop.sh
mirroring the cold-topic-explainer pattern. Launches an isolated
synapbus instance on port 18089, drives the demo, renders
report.html, cleans up. Full README documenting what's real vs
deferred, plus examples/README.md listing both examples.
- specs/018: tasks.md updated with MVP completion status; legacy tasks
naming collision noted.
Deferred (marked explicitly in example README):
- Real LLM-driven coordinator (needs MCP tool wiring + prompt
iteration)
- Real subprocess runs (needs reactor integration with task_id on
ExecRequest)
- Full MCP tool surface (contracts are written at
specs/018-dynamic-agent-spawning/contracts/mcp-tools.md)
- Svelte /goals UI (REST endpoints remain a follow-up)
- Full budget race + quarantine auto-trigger wiring
- Full resource-request → secrets fulfill reaction-workflow path
Cross-compiles clean for linux/amd64 and darwin/arm64 with no CGO
(SC-010). All new package tests pass (SC-004, SC-005, SC-007).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.7 KiB
doc-gardener
End-to-end demo of the dynamic agent spawning feature (spec 018-dynamic-agent-spawning).
A human owner defines a high-level goal ("verify docs.mcpproxy.app against the mcpproxy source code"). A pre-built coordinator meta-agent decomposes the goal into a task tree, proposes spawning specialist sub-agents with capped autonomy, the specialists claim tasks and produce artifacts, and a rich HTML report is generated from the run.
This example exercises the feature's data primitives end-to-end: goal creation with a backing channel, task-tree materialization with denormalized ancestry, config_hash-rooted trust, delegation-cap enforcement, atomic task claim, append-only reputation ledger, cost rollup, HTML rendering from DB state.
Status of the MVP demo
| Piece | Status |
|---|---|
| Goal creation + backing channel | ✅ real |
| Task tree materialization (ancestry snapshots) | ✅ real |
| Atomic optimistic-lock task claim | ✅ real (covered by 50-goroutine race test in internal/goaltasks/) |
| Config-hash computation (deterministic, sensitive to capability changes) | ✅ real (tested in internal/trust/) |
| Delegation cap enforcement (child ≤ parent) | ✅ real (tested in internal/trust/) |
| Append-only reputation ledger with 70 %-of-parent seed and exponential decay | ✅ real (tested in internal/trust/) |
| Cost rollup via recursive CTE | ✅ real (tested in internal/goaltasks/) |
| Rich HTML report (goal / tree / agents / costs / timeline) | ✅ real |
| Secret encryption + scoped env injection | ✅ real (internal/secrets/, tested) |
| Coordinator driven by a real LLM | ❌ deferred — the demo's coordinator logic lives in Go (cmd/docgardener/flow.go); the LLM-in-the-loop path needs MCP tool wiring + reactor integration |
| Specialist subprocess runs via the harness | ❌ deferred — the demo produces synthetic artifacts |
Full MCP tool surface (create_goal, propose_task_tree, propose_agent, claim_task, verify_task, request_resource, list_resources) |
❌ contracts live in specs/018-dynamic-agent-spawning/contracts/mcp-tools.md; wiring is deferred |
Svelte /goals UI |
❌ deferred |
See specs/018-dynamic-agent-spawning/tasks.md for the full phase breakdown and what remains.
Prereqs
- Go 1.25+
sqlite3,curlon$PATH- A free TCP port (default
18089)
Run it
./start.sh # build + launch synapbus on port 18089
./run_task.sh # execute the demo flow
./report.sh # render report.html
./stop.sh # shut down synapbus
run_task.sh can be re-run any number of times against a running instance — each invocation creates a new goal + task tree + reputation evidence, all appended to the ledger.
What happens under the hood
./run_task.sh invokes ./bin/docgardener run which:
- Bootstraps: creates user
algis(passwordalgis-demo-pw), creates theapprovalsandrequestschannels, and materializes the pre-built coordinator agent (doc-gardener-coordinator) with itsconfig_hashcomputed from its system prompt and tool scope. - Creates a goal via
goals.Service.CreateGoal— slugkeep-docs-mcpproxy-app-accurate-against-source, budget$50.00,max_spawn_depth=3. Auto-creates the#goal-...backing channel. - Decomposes the goal into a 4-node task tree (root +
scan-docs+verify-cli+drift-reportleaves) viagoaltasks.Service.CreateTree, which denormalizes the full ancestry onto each child task in a single transaction. - Spawns specialists — three agents (
docs-scanner,cli-verifier,drift-reporter), each one running throughtrust.DelegationCap()to verify its proposed grant does not exceed the coordinator's, then computing a deterministictrust.ConfigHash(...)and seeding its reputation ledger at 70 % of the parent's rolling score viatrust.Ledger.SeedFromParent(). - Atomically claims tasks — each specialist invokes
goaltasks.Service.Claim()which runs the optimistic-lockUPDATE ... WHERE assignee_agent_id IS NULL AND status='approved'pattern. A concurrent-claim race test ininternal/goaltasks/service_test.goverifies exactly-one-winner over 50 goroutine rounds. - Runs specialists — simulated for the v1 demo. Each task:
- transitions
claimed → in_progress → awaiting_verification → done - increments leaf spend (
tokens,dollars_cents) - posts an artifact message (
#finding,#verified,#summary) to the goal channel withmetadata.kind="artifact" - appends a positive evidence row to the reputation ledger with
score_delta=+0.15(auto verifier) or+0.2(command verifier)
- transitions
- Marks the goal completed.
./report.sh then invokes ./bin/docgardener report, which:
- reads the goal id from
.last_goal_id - queries all tasks, agents, reputation, messages, billing codes for that goal
- computes rolling reputation via
trust.Ledger.RollingScore()(exponential decay,half_life_days=30) - builds a recursive task tree + a chronological timeline
- renders
report.html.tmplintoreport.html - opens it in the default browser
Inspect during / after the run
- Web UI: http://localhost:18089 — log in as
algis/algis-demo-pw. The existing channels, messages, and agents views all work on the new data. - DB shell:
sqlite3 ./data/synapbus.db -header -column " SELECT id, title, status, spent_dollars_cents, assignee_agent_id FROM goal_tasks; " - Trust ledger:
sqlite3 ./data/synapbus.db -header -column " SELECT substr(config_hash,1,12) AS hash, score_delta, evidence_ref, created_at FROM reputation_evidence ORDER BY created_at; " - Cost rollup:
sqlite3 ./data/synapbus.db -header -column " SELECT COALESCE(billing_code,''), SUM(spent_tokens), SUM(spent_dollars_cents) FROM goal_tasks GROUP BY billing_code; "
Expected HTML report
report.html contains six sections:
- Header — goal title, status, budget, owner, backing channel
- Spend metrics — total dollars / tokens / agents spawned
- Goal description
- Task tree — recursive, collapsible, status badges, per-task spend, verifier kind
- Spawned agents — each with name,
config_hash(first 12 chars), parent agent, spawn depth, autonomy tier, rolling reputation bar, tool-scope chips, truncated system prompt - Cost breakdown by billing code — per-code task count, tokens, dollars
- Artifacts posted by specialists — the raw
#finding,#verified,#summarymessages - Timeline — every message in the goal channel, chronologically, annotated with actor and kind
Screenshot-equivalent output (minus images):
Doc-gardener run — Keep docs.mcpproxy.app accurate against source
Goal #4 · slug keep-docs-... · owner algis · backing channel #goal-... · [completed]
Spend Tokens Agents spawned
$1.05 6000 4
Task tree
├─ Verify docs.mcpproxy.app against source [approved]
│ ├─ Scan docs for CLI flags [done] $0.45 · 1500 tok · auto
│ ├─ Verify flags exist in mcpproxy binary [done] $0.25 · 2000 tok · auto
│ └─ Produce drift report [done] $0.35 · 2500 tok · command
Spawned agents
• Doc-gardener Coordinator config_hash 70a9a06e9595… root · assisted · rep 80%
• Docs Scanner config_hash a0b5c6538b2d… parent=coordinator · depth 1 · assisted · rep 58%
• CLI Verifier config_hash 47c6839eed73… parent=coordinator · depth 1 · assisted · rep 58%
• Drift Reporter config_hash ceaa7816aa42… parent=coordinator · depth 1 · assisted · rep 59%
Cost breakdown
doc-gardener 1 task 0 tok $0.00
doc-gardener/report 1 task 2500 tok $0.35
doc-gardener/scan 1 task 1500 tok $0.45
doc-gardener/verify 1 task 2000 tok $0.25
Tests for the primitives
The feature ships with passing test suites for every critical invariant:
go test ./internal/goals/... ./internal/goaltasks/... ./internal/trust/... ./internal/secrets/...
internal/goaltasks/service_test.goTestCreateTree_AncestryAndDepth— recursive tree build with correct depth + ancestryTestCreateTree_AncestryOverflow— 16 KB cap enforcementTestClaimAtomic_Race— 50 rounds × 2 racing goroutines, exactly one winner per roundTestRollupCosts— recursive CTE over 4-level treeTestTransition_StateMachine— legal and illegal transitions
internal/trust/config_hash_test.go— determinism under shuffled inputs, sensitivity to capability changesinternal/trust/delegation_test.go— full tier-matrix + tool-scope subset enforcementinternal/trust/ledger_test.go— exponential decay, 70%-of-parent seed, clampinginternal/secrets/store_test.go— NaCl roundtrip, scope precedence, name sanitization
Troubleshooting
| Symptom | Fix |
|---|---|
./start.sh fails at "admin socket never appeared" |
Another instance on port 18089 — set SYNAPBUS_PORT=18090 ./start.sh |
./run_task.sh fails with "DB not found" |
./start.sh hasn't run — run it first |
| Report page is empty or missing sections | .last_goal_id is stale — rerun ./run_task.sh then ./report.sh |
| Stale binary | rm -rf bin && ./start.sh — forces rebuild |
Next steps (out of scope for this MVP)
The spec at specs/018-dynamic-agent-spawning/ lays out what comes after this demo, including the full MCP tool surface, reactor integration for real subprocess runs, the Svelte /goals page, the resource-request protocol, quarantine on low reputation, and the LLM-driven coordinator. This example establishes that the foundational primitives work; the follow-up work layers on top.