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>
74 lines
3.9 KiB
SQL
74 lines
3.9 KiB
SQL
-- 021: Goals and tasks — first-class data model for dynamic agent spawning.
|
|
--
|
|
-- Goals are human-owned top-level objectives. Each goal has a backing
|
|
-- #goal-<slug> channel and a pre-built coordinator agent.
|
|
--
|
|
-- Tasks are nodes in a goal's work tree. They are single-assignee,
|
|
-- atomically claimable (optimistic-lock UPDATE), carry a denormalized
|
|
-- goal-ancestry JSON snapshot, and accumulate leaf-only cost counters
|
|
-- that roll up via a recursive CTE at read time.
|
|
|
|
CREATE TABLE goals (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
slug TEXT NOT NULL UNIQUE,
|
|
title TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
owner_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
channel_id INTEGER NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
|
coordinator_agent_id INTEGER REFERENCES agents(id) ON DELETE SET NULL,
|
|
root_task_id INTEGER,
|
|
status TEXT NOT NULL DEFAULT 'draft'
|
|
CHECK (status IN ('draft','active','paused','completed','cancelled','stuck')),
|
|
budget_tokens INTEGER,
|
|
budget_dollars_cents INTEGER,
|
|
max_spawn_depth INTEGER NOT NULL DEFAULT 3,
|
|
alert_80pct_posted INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
completed_at DATETIME
|
|
);
|
|
|
|
CREATE INDEX idx_goals_owner ON goals(owner_user_id);
|
|
CREATE INDEX idx_goals_status ON goals(status);
|
|
CREATE INDEX idx_goals_channel ON goals(channel_id);
|
|
|
|
CREATE TABLE goal_tasks (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
goal_id INTEGER NOT NULL REFERENCES goals(id) ON DELETE CASCADE,
|
|
parent_task_id INTEGER REFERENCES goal_tasks(id) ON DELETE CASCADE,
|
|
ancestry_json TEXT NOT NULL DEFAULT '[]',
|
|
depth INTEGER NOT NULL DEFAULT 0,
|
|
title TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
acceptance_criteria TEXT NOT NULL DEFAULT '',
|
|
created_by_agent_id INTEGER REFERENCES agents(id) ON DELETE SET NULL,
|
|
created_by_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
|
|
assignee_agent_id INTEGER REFERENCES agents(id) ON DELETE SET NULL,
|
|
status TEXT NOT NULL DEFAULT 'proposed'
|
|
CHECK (status IN ('proposed','approved','claimed','in_progress',
|
|
'awaiting_verification','done','failed','cancelled')),
|
|
billing_code TEXT,
|
|
budget_tokens INTEGER,
|
|
budget_dollars_cents INTEGER,
|
|
spent_tokens INTEGER NOT NULL DEFAULT 0,
|
|
spent_dollars_cents INTEGER NOT NULL DEFAULT 0,
|
|
heartbeat_config_json TEXT,
|
|
verifier_config_json TEXT,
|
|
origin_message_id INTEGER REFERENCES messages(id) ON DELETE SET NULL,
|
|
claim_message_id INTEGER REFERENCES messages(id) ON DELETE SET NULL,
|
|
completion_message_id INTEGER REFERENCES messages(id) ON DELETE SET NULL,
|
|
failure_reason TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
approved_at DATETIME,
|
|
claimed_at DATETIME,
|
|
started_at DATETIME,
|
|
completed_at DATETIME,
|
|
CHECK (created_by_agent_id IS NOT NULL OR created_by_user_id IS NOT NULL)
|
|
);
|
|
|
|
CREATE INDEX idx_goal_tasks_goal ON goal_tasks(goal_id);
|
|
CREATE INDEX idx_goal_tasks_parent ON goal_tasks(parent_task_id);
|
|
CREATE INDEX idx_goal_tasks_assignee ON goal_tasks(assignee_agent_id);
|
|
CREATE INDEX idx_goal_tasks_status ON goal_tasks(status);
|
|
CREATE INDEX idx_goal_tasks_billing ON goal_tasks(billing_code);
|