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>
388 lines
31 KiB
Markdown
388 lines
31 KiB
Markdown
---
|
||
description: "Task list for Dynamic Agent Spawning"
|
||
---
|
||
|
||
# Tasks: Dynamic Agent Spawning
|
||
|
||
**Input**: Design documents from `/specs/018-dynamic-agent-spawning/`
|
||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md
|
||
|
||
**Tests**: Included — the spec mandates table-driven unit tests, service-layer integration tests, and end-to-end MCP tests (success criteria SC-004, SC-005, SC-006, SC-007, SC-008, SC-009, SC-012).
|
||
|
||
**Organization**: Tasks are grouped by user story. Setup and Foundational phases run first; US1 is the MVP slice.
|
||
|
||
## Format: `[ID] [P?] [Story] Description`
|
||
|
||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||
- **[Story]**: Which user story this task belongs to (US1–US9 from spec.md)
|
||
- File paths are absolute-from-repo-root
|
||
|
||
## Path Conventions
|
||
|
||
- **Backend code**: `internal/<package>/` and `cmd/synapbus/`
|
||
- **Schema**: `internal/storage/schema/`
|
||
- **Web UI source**: `web/src/`
|
||
- **Tests**: co-located `_test.go` files in the same package
|
||
- **Example**: `examples/doc-gardener/`
|
||
|
||
---
|
||
|
||
## Phase 1: Setup (Shared Infrastructure)
|
||
|
||
**Purpose**: Create new packages and migration files; wire through the build. No behavior yet.
|
||
|
||
- [ ] T001 Create empty Go packages with `doc.go` files at `internal/goals/doc.go`, `internal/tasks/doc.go`, `internal/trust/doc.go`, `internal/secrets/doc.go`
|
||
- [X] T002 [P] Create migration files (empty DDL, just table shells per data-model.md) at `internal/storage/schema/021_goals_tasks.sql`, `internal/storage/schema/022_agent_proposals.sql`, `internal/storage/schema/023_agent_trust_model.sql`, `internal/storage/schema/024_secrets.sql`, `internal/storage/schema/025_harness_runs_task_id.sql` — **note**: table renamed from `tasks` to `goal_tasks` (legacy `tasks` table exists from migration 001 for channel task auctions)
|
||
- [ ] T003 [P] Create example scaffolding at `examples/doc-gardener/` mirroring `examples/cold-topic-explainer/` — copy `start.sh`, `stop.sh`, `run_task.sh`, `wrapper.sh`, `README.md` with doc-gardener placeholders
|
||
- [ ] T004 [P] Add `golang.org/x/crypto/nacl/secretbox` to `go.mod` via `go get` and verify the pure-Go build still works (`CGO_ENABLED=0 go build ./...`)
|
||
- [ ] T005 Create `specs/018-dynamic-agent-spawning/BUILD_NOTES.md` with the developer runbook (build commands, test commands, example commands) for the whole feature
|
||
|
||
---
|
||
|
||
## Phase 2: Foundational (Blocking Prerequisites)
|
||
|
||
**Purpose**: Migrations, base types, and the trust primitive that every user story depends on.
|
||
|
||
- [X] T010 Fill migration 021 DDL (goals + goal_tasks tables, indexes, state CHECK constraints)
|
||
- [X] T011 Fill migration 022 DDL (agent_proposals + resource_requests tables)
|
||
- [X] T012 Fill migration 023 DDL — **revised**: keep existing agent_trust table intact (wired to reactions), only ALTER agents + CREATE reputation_evidence
|
||
- [X] T013 Fill migration 024 DDL (secrets table)
|
||
- [X] T014 Fill migration 025 DDL (harness_runs.task_id)
|
||
- [ ] T015 [P] Write data-migration Go code that extracts `system_prompt` from existing `harness_config_json` for pre-existing agents, idempotent, in `internal/storage/migrations_data.go`
|
||
- [ ] T016 [P] Write data-migration Go code that computes `config_hash` for every existing agent and backfills the column in `internal/storage/migrations_data.go`
|
||
- [ ] T017 Implement `trust.ConfigHash(agent)` canonical SHA-256 hashing (sorted-keys JSON) in `internal/trust/hash.go` with table-driven unit tests in `internal/trust/hash_test.go` verifying stability across shuffled input arrays
|
||
- [ ] T018 Implement `trust.AppendEvidence()`, `trust.RollingScore()` (with exponential decay, clamp [0,1], neutral 0.5 default), `trust.IsQuarantined()` in `internal/trust/ledger.go` with unit tests in `internal/trust/ledger_test.go`
|
||
- [ ] T019 [P] Implement `trust.DelegationCap(parent, proposed) (Grant, violations)` enforcing the child ≤ parent rule across tool_scope, autonomy_tier, and budget dimensions in `internal/trust/delegation.go` with a full tier-matrix test in `internal/trust/delegation_test.go` — **covers SC-005**
|
||
- [ ] T020 Extend `internal/agents/types.go` with the new columns (`ConfigHash`, `ParentAgentID`, `SpawnDepth`, `SystemPrompt`, `AutonomyTier`, `ToolScope`, `QuarantinedAt`)
|
||
- [ ] T021 Extend `internal/agents/service.go` with `RecomputeConfigHash(ctx, agentID)` that recomputes on any config mutation and re-seeds reputation at 70 % of prior value — includes unit test verifying the 70 % invariant in `internal/agents/service_test.go` — **covers SC-007**
|
||
- [ ] T022 Implement `secrets.Store` in `internal/secrets/store.go` with NaCl secretbox encrypt/decrypt, master key file bootstrap (0600 perms, auto-generate on first use), CRUD, scope-filter, env sanitization; unit tests in `internal/secrets/store_test.go`
|
||
- [ ] T023 Implement `secrets.BuildEnv(ctx, agentID, taskID) map[string]string` in `internal/secrets/injector.go` with precedence user < agent < task; unit test in `internal/secrets/injector_test.go`
|
||
|
||
---
|
||
|
||
## Phase 3: User Story 1 — Create a goal from free text (Priority: P1) 🎯 MVP
|
||
|
||
**Goal**: A human can run `create_goal` and get back a goal id, a `#goal-<slug>` channel, and a coordinator agent wired up.
|
||
|
||
**Independent Test**: Call `create_goal` via the MCP transport with title + description; assert `goals` row exists, channel exists, owner is a member, and a coordinator agent is referenced.
|
||
|
||
**Maps to**: Spec US1, FR-001, FR-002, SC-001.
|
||
|
||
- [ ] T030 [US1] Implement `Goal` struct, `GoalStatus` enum, and repository CRUD in `internal/goals/types.go` and `internal/goals/repo.go`
|
||
- [ ] T031 [US1] Implement `goals.Service.CreateGoal(ctx, title, description, ownerID, opts)` that atomically inserts the goal, generates a unique slug with collision-dedup suffixing, creates the `#goal-<slug>` channel via existing `channels.Service`, adds the owner as a member, and assigns the coordinator — in `internal/goals/service.go`
|
||
- [ ] T032 [US1] Implement `goals.Service.GetGoal`, `ListGoals`, `TransitionTo` with state-machine guarding in `internal/goals/service.go`
|
||
- [ ] T033 [P] [US1] Write table-driven unit tests for `goals.Service` covering happy path, slug collision, invalid status transitions in `internal/goals/service_test.go`
|
||
- [ ] T034 [P] [US1] Write real-SQLite integration test `TestCreateGoal_EndToEnd` verifying DB row, channel creation, member add, coordinator assignment in `internal/goals/integration_test.go`
|
||
- [ ] T035 [US1] Implement MCP tool `create_goal` handler in `internal/mcp/tools_goals.go` with JSON Schema matching `contracts/mcp-tools.md` §1
|
||
- [ ] T036 [P] [US1] Write MCP tool test `TestCreateGoalMCP` using the existing in-process MCP test harness in `internal/mcp/tools_goals_test.go`
|
||
- [ ] T037 [US1] Register `create_goal` in the MCP tool registry in `internal/mcp/server.go`
|
||
- [ ] T038 [P] [US1] Add REST endpoint `GET /api/goals` and `GET /api/goals/:id` in `internal/api/handlers_goals.go`, returning the JSON snapshot defined in `contracts/mcp-tools.md` §REST
|
||
- [ ] T039 [P] [US1] Write REST handler tests in `internal/api/handlers_goals_test.go`
|
||
- [ ] T040 [P] [US1] Create Svelte 5 route `web/src/routes/goals/+page.svelte` listing goals (title, status, owner, total spend, last activity) consuming `GET /api/goals`
|
||
- [ ] T041 [P] [US1] Create Svelte 5 route `web/src/routes/goals/[id]/+page.svelte` with placeholder goal header (task tree will come in US2)
|
||
- [ ] T042 [US1] Rebuild embedded Web UI via `make web` and verify `internal/web/dist/` is regenerated
|
||
- [ ] T043 [US1] Wire a `create_goal` helper into the example script `examples/doc-gardener/run_task.sh` that calls the admin socket to create the doc-gardener goal
|
||
|
||
**Checkpoint**: `./start.sh && synapbus goals create --title "Test" --desc "Test goal"` works; Web UI `/goals` page lists it.
|
||
|
||
---
|
||
|
||
## Phase 4: User Story 2 — Coordinator decomposes a goal (Priority: P1)
|
||
|
||
**Goal**: The coordinator agent posts a task-tree proposal; the human approves; tasks are materialized atomically with denormalized ancestry.
|
||
|
||
**Independent Test**: Create a goal, wait for coordinator heartbeat, assert a `task_proposal` message in `#approvals`. React `approve`. Assert `tasks` rows with correct parent/child links, depth, and ancestry JSON.
|
||
|
||
**Maps to**: Spec US2, FR-003, FR-004, FR-006, FR-008, FR-033.
|
||
|
||
- [ ] T050 [US2] Implement `Task` struct, `TaskStatus` enum, `AncestryNode`, `VerifierConfig`, `HeartbeatConfig` types in `internal/tasks/types.go`
|
||
- [ ] T051 [US2] Implement `tasks.Service.CreateTaskTree(ctx, goalID, proposerID, tree)` that walks the proposed tree, assigns depth, builds ancestry snapshot (≤16 KB cap check), inserts all rows in a single transaction, and posts a single "tree materialized" system message to the goal channel — in `internal/tasks/service.go`
|
||
- [ ] T052 [US2] Implement `tasks.Service.TransitionTo(ctx, taskID, newStatus)` with state-machine guarding and automatic system-message posting to the goal channel — in `internal/tasks/service.go`
|
||
- [ ] T053 [P] [US2] Write table-driven unit tests for `CreateTaskTree` (depth assignment, ancestry correctness, 16 KB overflow rejection) and `TransitionTo` (valid and invalid transitions) in `internal/tasks/service_test.go`
|
||
- [ ] T054 [P] [US2] Write real-SQLite integration test `TestTaskTreeMaterialization` verifying atomicity (partial failure rolls back) in `internal/tasks/integration_test.go`
|
||
- [ ] T055 [US2] Implement MCP tool `propose_task_tree` handler in `internal/mcp/tools_tasks.go` per `contracts/mcp-tools.md` §2 — writes approval message; wires the reaction-workflow callback to invoke `tasks.Service.CreateTaskTree` on approval
|
||
- [ ] T056 [P] [US2] Write MCP tool test `TestProposeTaskTreeMCP` in `internal/mcp/tools_tasks_test.go`
|
||
- [ ] T057 [US2] Register `propose_task_tree` in `internal/mcp/server.go`
|
||
- [ ] T058 [US2] Extend `internal/api/handlers_goals.go` `GET /api/goals/:id` response to include the materialized task tree (recursive CTE fetch)
|
||
- [ ] T059 [P] [US2] Extend `web/src/routes/goals/[id]/+page.svelte` with a recursive Svelte task-tree component (collapsible, status badges, per-task spend) and wire the SSE event stream for real-time updates
|
||
- [ ] T060 [US2] Rebuild embedded Web UI via `make web`
|
||
- [ ] T061 [US2] Write the coordinator's fixed system prompt in `internal/coordinator/default_prompt.md` covering: role, allowed tools, JSON output format for tree proposals, delegation cap reminder — per research.md D13
|
||
- [ ] T062 [US2] Ship the pre-built coordinator config (name, model, system prompt from T061, tool scope) in `examples/doc-gardener/configs/coordinator.json`
|
||
- [ ] T063 [US2] Create `auto_approve.sh` helper in `examples/doc-gardener/auto_approve.sh` that polls `#approvals` every 1 s and posts `approve` reactions
|
||
|
||
**Checkpoint**: Running the example with `--auto-approve` produces a task tree in `tasks` table and a timeline entry in the goal channel.
|
||
|
||
---
|
||
|
||
## Phase 5: User Story 3 — Propose spawning a specialist agent (Priority: P1)
|
||
|
||
**Goal**: Coordinator calls `propose_agent`; server pre-checks delegation cap + spawn depth; human approves; new `agents` row is materialized with `config_hash`, `parent_agent_id`, `spawn_depth`, seeded reputation.
|
||
|
||
**Independent Test**: Call `propose_agent` from the coordinator with a valid proposal and a violating proposal (tool scope exceeds parent). Assert the violating one is rejected server-side. Approve the valid one. Assert new `agents` row with hash, parent, depth, and reputation seed.
|
||
|
||
**Maps to**: Spec US3, FR-010, FR-011, FR-012, FR-013, FR-014, FR-015, FR-016, FR-017, FR-018.
|
||
|
||
- [ ] T070 [US3] Implement `AgentProposal` type and repository in `internal/agents/proposals.go`
|
||
- [ ] T071 [US3] Implement `agents.ProposalService.Propose(ctx, proposal)` that runs delegation-cap pre-check (via `trust.DelegationCap`), depth-cap check, then writes `agent_proposals` row and posts `#approvals` message — in `internal/agents/proposals.go`
|
||
- [ ] T072 [US3] Implement `agents.ProposalService.Approve(ctx, proposalID, approverID)` that runs inside a transaction: (a) re-check caps; (b) compute `config_hash`; (c) insert `agents` row with parent/depth; (d) mint new API key; (e) seed `reputation_evidence` at 70 % of parent's rolling score; (f) DM proposer with credentials — in `internal/agents/proposals.go`
|
||
- [ ] T073 [US3] Implement `agents.ProposalService.Reject(ctx, proposalID, reason)` with DM to proposer
|
||
- [ ] T074 [P] [US3] Write table-driven unit tests covering: valid proposal, depth-cap violation, delegation-cap violation (all tier/tool combinations) in `internal/agents/proposals_test.go` — **covers SC-005**
|
||
- [ ] T075 [P] [US3] Write integration test `TestProposeAndMaterialize` with real SQLite in `internal/agents/proposals_integration_test.go`
|
||
- [ ] T076 [US3] Implement MCP tool `propose_agent` handler in `internal/mcp/tools_spawn.go` per `contracts/mcp-tools.md` §3
|
||
- [ ] T077 [P] [US3] Write MCP test `TestProposeAgentMCP` in `internal/mcp/tools_spawn_test.go`
|
||
- [ ] T078 [US3] Register `propose_agent` in `internal/mcp/server.go`
|
||
- [ ] T079 [US3] Wire the reaction-workflow callback so that an `approve` reaction on an `#approvals` message of type `agent_proposal` calls `ProposalService.Approve`
|
||
- [ ] T080 [P] [US3] Add "spawned agents" panel to `web/src/routes/goals/[id]/+page.svelte` showing each agent's `config_hash` (first 12 chars), parent, autonomy tier, and rolling reputation
|
||
- [ ] T081 [US3] Update the coordinator's system prompt in `internal/coordinator/default_prompt.md` to include the `propose_agent` tool description and the expected JSON format
|
||
- [ ] T082 [US3] Ship three specialist templates in `examples/doc-gardener/configs/` — `docs-scanner.json`, `cli-verifier.json`, `commit-watcher.json` (per research.md D16, with model, prompt, tool scope, MCP)
|
||
|
||
**Checkpoint**: Running the example produces a task tree, then coordinator proposes 1–3 specialists which are auto-approved and materialized.
|
||
|
||
---
|
||
|
||
## Phase 6: User Story 4 — Specialist claims a task and runs on a heartbeat (Priority: P1)
|
||
|
||
**Goal**: Atomic claim + reactor wakes the agent + harness runs the subprocess with injected context + task transitions to `awaiting_verification`.
|
||
|
||
**Independent Test**: Given an approved task and a matching specialist, call `claim_task`; wait for heartbeat; assert `harness_runs` row with `task_id`, leaf-task spend updated, task at `awaiting_verification`.
|
||
|
||
**Maps to**: Spec US4, FR-005, FR-007, FR-009, FR-028, FR-037, FR-038, FR-039.
|
||
|
||
- [ ] T090 [US4] Implement `tasks.Service.ClaimTask(ctx, taskID, agentID)` as the atomic optimistic-lock UPDATE per data-model.md, returning `ErrAlreadyClaimed` on 0 rows affected — in `internal/tasks/service.go`
|
||
- [ ] T091 [P] [US4] Write concurrency integration test `TestClaimTask_Concurrent` spawning 100 goroutines racing on the same task, asserting exactly one winner — in `internal/tasks/claim_test.go` — **covers SC-004**
|
||
- [ ] T092 [US4] Implement `tasks.Service.RollupCosts(ctx, rootTaskID)` using the recursive CTE from data-model.md — in `internal/tasks/service.go`
|
||
- [ ] T093 [P] [US4] Write test `TestRollupCosts_Recursive` building a 4-level tree and verifying sums — in `internal/tasks/rollup_test.go`
|
||
- [ ] T094 [US4] Implement MCP tool `claim_task` in `internal/mcp/tools_tasks.go` per `contracts/mcp-tools.md` §4
|
||
- [ ] T095 [P] [US4] Write MCP test `TestClaimTaskMCP` (including `ErrAlreadyClaimed` and `ErrAgentQuarantined` paths) in `internal/mcp/tools_tasks_test.go`
|
||
- [ ] T096 [US4] Extend `internal/harness/reactor/reactor.go` with three new wake sources (`task_assignment`, `task_timer`, `verification_requested`); reuse existing `pending_work` coalescing
|
||
- [ ] T097 [US4] Wire the reactor to invoke `secrets.BuildEnv(ctx, agentID, taskID)` when preparing subprocess env for a task-context run, and to write `harness_runs.task_id` on completion
|
||
- [ ] T098 [US4] Wire the reactor's post-run path to increment the leaf task's `spent_tokens` / `spent_dollars_cents` and transition the task to `awaiting_verification` inside the same transaction as the `harness_runs` insert
|
||
- [ ] T099 [P] [US4] Write integration test `TestReactorTaskHeartbeat_EndToEnd` using a mock subprocess that emits a synthetic cost, asserting the task state machine advances and `harness_runs.task_id` is set — in `internal/harness/reactor/reactor_task_test.go`
|
||
- [ ] T100 [P] [US4] Extend the `docs-scanner` and `cli-verifier` specialist wrapper scripts in `examples/doc-gardener/wrapper.sh` to read `SYNAPBUS_TASK_ID`, `SYNAPBUS_TASK_DESCRIPTION`, `SYNAPBUS_TASK_ANCESTRY` env vars set by the harness
|
||
- [ ] T101 [US4] Propagate OTel trace context across the reactor → subprocess hop (reuse existing TRACEPARENT plumbing from fee73e3)
|
||
|
||
**Checkpoint**: The example runs through claim → heartbeat → subprocess → completion message posted to goal channel.
|
||
|
||
---
|
||
|
||
## Phase 7: User Story 5 — Verification gate (Priority: P1)
|
||
|
||
**Goal**: Tasks in `awaiting_verification` are verified via one of `auto`, `peer`, `command` verifier kinds; verdicts drive `done`/`failed`; reputation is appended on success.
|
||
|
||
**Independent Test**: Configure a task with each verifier kind; transition through verification; assert correct terminal state and reputation row appended.
|
||
|
||
**Maps to**: Spec US5, FR-034, FR-035, FR-036.
|
||
|
||
- [ ] T110 [US5] Implement the verifier dispatcher in `internal/tasks/verifier.go` — parses `verifier_config_json`, routes to auto/peer/command handler, writes the resulting `tasks.status`
|
||
- [ ] T111 [US5] Implement `peer` verifier: call the configured peer's `verify_task` tool via the in-process MCP client in `internal/tasks/verifier_peer.go`
|
||
- [ ] T112 [US5] Implement `command` verifier: shell out with task env, enforce timeout, exit code 0 → approve in `internal/tasks/verifier_command.go`
|
||
- [ ] T113 [US5] On verdict, append `reputation_evidence` row with the appropriate `score_delta` (+1.0 approve / -1.0 reject) via `trust.AppendEvidence` — wire in `internal/tasks/verifier.go`
|
||
- [ ] T114 [US5] On verification failure, DM the coordinator of the task's goal with task id, verdict, and reason
|
||
- [ ] T115 [P] [US5] Write unit test `TestVerifier_AutoApprove`, `TestVerifier_CommandExitCodes`, `TestVerifier_PeerApprove_PeerReject` in `internal/tasks/verifier_test.go`
|
||
- [ ] T116 [US5] Implement MCP tool `verify_task` in `internal/mcp/tools_tasks.go` per `contracts/mcp-tools.md` §5 — gated on the caller being the configured peer verifier
|
||
- [ ] T117 [P] [US5] Write MCP test `TestVerifyTaskMCP` with positive and negative paths in `internal/mcp/tools_tasks_test.go`
|
||
- [ ] T118 [US5] Register `verify_task` in `internal/mcp/server.go`
|
||
- [ ] T119 [US5] Wire the reactor to invoke the verifier dispatcher as a new wake source `verification_requested` when a task enters `awaiting_verification`
|
||
|
||
**Checkpoint**: A task with each verifier kind runs through to `done` or `failed`; reputation ledger grows.
|
||
|
||
---
|
||
|
||
## Phase 8: User Story 9 — Doc-gardener example runs end-to-end (Priority: P1)
|
||
|
||
**Goal**: `./start.sh && ./run_task.sh --auto-approve && ./report.sh` produces `report.html` showing the full run.
|
||
|
||
**Independent Test**: Run the script trio from a clean checkout; assert `report.html` exists and contains the required sections.
|
||
|
||
**Maps to**: Spec US9, FR-043, FR-044, FR-045, FR-046, SC-002, SC-003.
|
||
|
||
> Note: User Story 9 is a P1 integration-level story — it depends on US1 through US5. It is placed after US5 deliberately so that the "doc-gardener runs" phase is the capstone of the MVP.
|
||
|
||
- [ ] T130 [US9] Complete `examples/doc-gardener/start.sh` per `quickstart.md` §1: preflight, rebuild, launch, create user `algis`, create coordinator, create `#approvals` + `#requests` channels
|
||
- [ ] T131 [US9] Complete `examples/doc-gardener/run_task.sh` per `quickstart.md` §2: DM coordinator, launch `auto_approve.sh`, poll goal channel for `FINAL:` message, 5-min timeout
|
||
- [ ] T132 [US9] Complete `examples/doc-gardener/stop.sh` per `cold-topic-explainer` pattern
|
||
- [ ] T133 [US9] Complete `examples/doc-gardener/auto_approve.sh` polling script
|
||
- [ ] T134 [US9] Write `examples/doc-gardener/wrapper.sh` subprocess entrypoint reading `SYNAPBUS_TASK_*` env vars and invoking the chosen LLM CLI
|
||
- [ ] T135 [P] [US9] Write the Go HTML report generator at `examples/doc-gardener/report.go` (cmd-line tool) that queries the DB via the internal `goals.Service.GetGoalSnapshot(id)` and renders the template
|
||
- [ ] T136 [P] [US9] Write the Go text/template at `examples/doc-gardener/report.html.tmpl` with the 6 sections from research.md D17 (header / tree / agents / cost / timeline / artifacts) with inline dark-mode CSS
|
||
- [ ] T137 [US9] Write `examples/doc-gardener/report.sh` that builds the report binary, runs it against the last goal id (from `.last_goal_id`), and opens `report.html`
|
||
- [ ] T138 [US9] Implement `goals.Service.GetGoalSnapshot(ctx, goalID) (*GoalSnapshot, error)` assembling goal + tree + agents + cost breakdown + timeline — in `internal/goals/snapshot.go`
|
||
- [ ] T139 [P] [US9] Write unit test `TestGoalSnapshot_ContainsAllSections` asserting every required field in `internal/goals/snapshot_test.go`
|
||
- [ ] T140 [US9] Write `examples/doc-gardener/README.md` per the `cold-topic-explainer/README.md` pattern, with the expected flow, commands, and troubleshooting from `quickstart.md`
|
||
- [ ] T141 [US9] End-to-end smoke test: run `./start.sh && ./run_task.sh --auto-approve && ./report.sh` against the current checkout; iterate on failures until it produces a valid report — **covers SC-002, SC-003**
|
||
|
||
**Checkpoint**: 🎯 **MVP DONE** — the doc-gardener example runs end-to-end and produces an HTML report. Ship-ready.
|
||
|
||
---
|
||
|
||
## Phase 9: User Story 6 — Resource-request protocol (Priority: P2)
|
||
|
||
**Goal**: Agents can ask the human for missing secrets; humans provide them via CLI; subsequent runs get the env var.
|
||
|
||
**Maps to**: Spec US6, FR-025, FR-026, FR-027, FR-028, FR-029, FR-030, SC-012.
|
||
|
||
- [ ] T150 [US6] Implement MCP tool `request_resource` in `internal/mcp/tools_resources.go` per `contracts/mcp-tools.md` §6
|
||
- [ ] T151 [P] [US6] Write MCP test `TestRequestResourceMCP` in `internal/mcp/tools_resources_test.go`
|
||
- [ ] T152 [US6] Implement MCP tool `list_resources` in `internal/mcp/tools_resources.go` per `contracts/mcp-tools.md` §7 (names-only, never values)
|
||
- [ ] T153 [P] [US6] Write MCP test `TestListResourcesMCP_NeverReturnsValues` asserting values are never exposed
|
||
- [ ] T154 [US6] Register both tools in `internal/mcp/server.go`
|
||
- [ ] T155 [US6] Implement cobra subcommand `synapbus secrets set|get|list|revoke` in `cmd/synapbus/cmd_secrets.go` — `get` returns availability only, never value; `set` encrypts and stores scoped
|
||
- [ ] T156 [P] [US6] Write cobra integration test in `cmd/synapbus/cmd_secrets_test.go`
|
||
- [ ] T157 [US6] Wire the resource-request workflow: on `secrets set` for a pending `resource_requests` row, mark `fulfilled`, DM the requester, and re-queue the agent for a heartbeat
|
||
- [ ] T158 [P] [US6] Write end-to-end integration test `TestRequestResource_RoundTrip` — agent requests, human sets, agent runs, subprocess sees env var — in `internal/secrets/e2e_test.go` — **covers SC-012**
|
||
- [ ] T159 [P] [US6] Add `/api/secrets/:scope_type/:scope_id` REST endpoint in `internal/api/handlers_secrets.go` returning names only; tests in `internal/api/handlers_secrets_test.go`
|
||
|
||
**Checkpoint**: A specialist can request a credential and the human can fulfill it without restarting anything.
|
||
|
||
---
|
||
|
||
## Phase 10: User Story 7 — Budget cascade, soft alert, auto-pause (Priority: P2)
|
||
|
||
**Goal**: Goals hit 80 % → soft alert; hit 100 % → paused; budgets cascade across delegation.
|
||
|
||
**Maps to**: Spec US7, FR-019, FR-020, FR-021, FR-022, SC-006.
|
||
|
||
- [ ] T170 [US7] Implement `goals.Service.CheckBudgetAndLaunch(ctx, goalID, newCost) error` that runs inside the same transaction as the `harness_runs` insert per research.md D7 — in `internal/goals/budget.go`
|
||
- [ ] T171 [US7] Implement soft-alert posting (idempotent, only on prior-below-80 + now-at-or-above-80) in `internal/goals/budget.go`
|
||
- [ ] T172 [US7] Implement hard auto-pause (transition goal to `paused`, post pause message, DM owner) in `internal/goals/budget.go`
|
||
- [ ] T173 [US7] Wire `goals.Service.CheckBudgetAndLaunch` into the reactor's pre-run transaction in `internal/harness/reactor/reactor.go`
|
||
- [ ] T174 [US7] Implement `tasks.Service.CreateSubTask` to subtract child budget from parent's remaining budget, failing with `ErrBudgetInsufficient` if over
|
||
- [ ] T175 [US7] Implement `goals.Service.SpendByBillingCode(ctx, goalID) (map[string]Cost, error)` rollup query
|
||
- [ ] T176 [P] [US7] Write integration test `TestBudgetRace_80AndPause` with two concurrent runs, asserting exactly one soft alert and the goal auto-pauses — in `internal/goals/budget_test.go` — **covers SC-006**
|
||
- [ ] T177 [P] [US7] Write unit test `TestBillingCodeRollup` asserting per-billing-code totals in `internal/goals/budget_test.go`
|
||
- [ ] T178 [US7] Implement MCP tool `resume_goal` in `internal/mcp/tools_goals.go` per `contracts/mcp-tools.md` §8
|
||
- [ ] T179 [P] [US7] Write MCP test `TestResumeGoalMCP` in `internal/mcp/tools_goals_test.go`
|
||
- [ ] T180 [US7] Register `resume_goal` in `internal/mcp/server.go`
|
||
- [ ] T181 [P] [US7] Add "Budget" + "Billing code breakdown" panels to `web/src/routes/goals/[id]/+page.svelte`
|
||
|
||
**Checkpoint**: Goals auto-pause at budget ceiling; the Web UI shows breakdowns.
|
||
|
||
---
|
||
|
||
## Phase 11: User Story 8 — Quarantine on low reputation (Priority: P3)
|
||
|
||
**Goal**: Agents whose rolling reputation drops below 0.3 are auto-quarantined and cannot claim tasks.
|
||
|
||
**Maps to**: Spec US8, FR-023, FR-024.
|
||
|
||
- [ ] T190 [US8] Implement `trust.CheckQuarantine(ctx, agentID)` in `internal/trust/ledger.go` running a ticker every N seconds (configurable, default 60) plus on every evidence append
|
||
- [ ] T191 [US8] Implement `agents.Service.Quarantine(ctx, agentID, reason)` and `Unquarantine(ctx, agentID)` in `internal/agents/service.go`
|
||
- [ ] T192 [US8] Reject `claim_task` from quarantined agents with `ErrAgentQuarantined` — wire in `internal/tasks/service.go`
|
||
- [ ] T193 [P] [US8] Write unit test `TestQuarantine_BelowThreshold` and `TestQuarantine_ClaimRejected` in `internal/agents/service_test.go`
|
||
- [ ] T194 [US8] Implement MCP tool `unquarantine_agent` in `internal/mcp/tools_goals.go` per `contracts/mcp-tools.md` §9
|
||
- [ ] T195 [P] [US8] Write MCP test `TestUnquarantineMCP` in `internal/mcp/tools_goals_test.go`
|
||
- [ ] T196 [US8] Register `unquarantine_agent` in `internal/mcp/server.go`
|
||
- [ ] T197 [P] [US8] Add "Quarantined" indicator to agent detail page in `web/src/routes/agents/[name]/+page.svelte`
|
||
|
||
**Checkpoint**: Reputation drop triggers quarantine; human can lift it via CLI or UI.
|
||
|
||
---
|
||
|
||
## Phase 12: Polish & Cross-Cutting Concerns
|
||
|
||
**Purpose**: Full test sweep, cross-compile verification, docs, and the finishing touches needed to ship.
|
||
|
||
- [ ] T210 Run the full test suite: `go test ./...` with `-race` and fix any flakes
|
||
- [ ] T211 Cross-compile verification: `CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./...` and `CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build ./...` — **covers SC-010**
|
||
- [ ] T212 [P] Verify all new MCP tools appear in `tools/list` via an MCP transport integration test in `internal/mcp/integration_test.go` — **covers SC-008**
|
||
- [ ] T213 [P] Web UI smoke test: load `/goals` with 100 seeded goals × 1000 tasks and assert load time < 1 s in `web/src/routes/goals/+page.svelte.test.ts` — **covers SC-011**
|
||
- [ ] T214 Update the project-level `CLAUDE.md` (already auto-updated by /speckit.plan) with any additional notes about running the doc-gardener example
|
||
- [ ] T215 Update `examples/README.md` (or create if absent) to list both `cold-topic-explainer` and `doc-gardener` with one-line descriptions and links
|
||
- [ ] T216 [P] Write a top-level `docs/dynamic-agents.md` explaining the trust model, spawn primitive, and verification workflow for new users
|
||
- [ ] T217 Run the complete SC checklist against the built binary and generated report — confirm every SC-0xx item is met
|
||
- [ ] T218 Commit the full branch with a descriptive message; push to GitHub
|
||
- [ ] T219 Final smoke: wipe `./examples/doc-gardener/data` and rerun `./start.sh && ./run_task.sh --auto-approve && ./report.sh` in one shot to confirm reproducibility from a clean state
|
||
- [ ] T220 Review `specs/018-dynamic-agent-spawning/checklists/requirements.md` — mark done after T219 passes
|
||
|
||
---
|
||
|
||
## Dependency Graph
|
||
|
||
```text
|
||
Phase 1 (Setup) — no dependencies
|
||
↓
|
||
Phase 2 (Foundational) — needs Phase 1
|
||
↓
|
||
Phase 3 (US1 — MVP start) — needs Phase 2
|
||
↓
|
||
Phase 4 (US2) — needs US1 (goal exists before tree)
|
||
↓
|
||
Phase 5 (US3) — needs US2 (tasks exist before spawn proposals target them)
|
||
↓
|
||
Phase 6 (US4) — needs US3 (specialists must exist before claims)
|
||
↓
|
||
Phase 7 (US5 — verification) — needs US4 (tasks must be in awaiting_verification)
|
||
↓
|
||
Phase 8 (US9 — doc-gardener E2E) — needs US1–US5 (MVP capstone)
|
||
↓ [MVP ships here]
|
||
Phase 9 (US6 — resources) ─┐ — needs US4 (run path exists); can run parallel to Phase 10 & 11
|
||
Phase 10 (US7 — budget) ──┤ — needs US4 (harness_runs path)
|
||
Phase 11 (US8 — quarantine) ┘ — needs Phase 2 (trust primitive)
|
||
↓
|
||
Phase 12 (Polish) — needs everything above
|
||
```
|
||
|
||
## Parallel Execution Opportunities
|
||
|
||
**Within Phase 1**: T002, T003, T004 run in parallel.
|
||
|
||
**Within Phase 2**: T015, T016 can run after migrations are written. T019 can run after T017 and T018. T022 and T023 run in parallel after T017.
|
||
|
||
**Within Phase 3 (US1)**: T033, T034, T036, T038, T039, T040, T041 all [P] — different files.
|
||
|
||
**Within Phase 4 (US2)**: T053, T054, T056, T059 all [P].
|
||
|
||
**Within Phase 5 (US3)**: T074, T075, T077, T080 all [P].
|
||
|
||
**Within Phase 6 (US4)**: T091, T093, T095, T099, T100 all [P].
|
||
|
||
**Within Phase 7 (US5)**: T115, T117 [P].
|
||
|
||
**Within Phase 8 (US9)**: T135, T136, T139 [P].
|
||
|
||
**Phase 9, 10, 11**: can run in parallel to each other after US5 is done. Inside each phase, [P] tasks run in parallel.
|
||
|
||
**Within Phase 12**: T212, T213, T216 [P].
|
||
|
||
## Implementation Strategy
|
||
|
||
### MVP Scope (ship-ready)
|
||
|
||
Phases 1, 2, 3, 4, 5, 6, 7, 8 — this gets you US1 through US5 and the doc-gardener example running end-to-end. **At the end of Phase 8 the feature is demoable and the HTML report renders.** This is the minimum to ship.
|
||
|
||
### Post-MVP
|
||
|
||
Phases 9, 10, 11 can be shipped as follow-up PRs without re-cutting the MVP. Phase 12 (polish) gates the final merge.
|
||
|
||
### Task Count
|
||
|
||
| Phase | Story | Count | Parallel |
|
||
|---|---|---|---|
|
||
| 1 — Setup | — | 5 | 3 |
|
||
| 2 — Foundational | — | 14 | 5 |
|
||
| 3 — US1 | P1 | 14 | 7 |
|
||
| 4 — US2 | P1 | 14 | 4 |
|
||
| 5 — US3 | P1 | 13 | 5 |
|
||
| 6 — US4 | P1 | 12 | 5 |
|
||
| 7 — US5 | P1 | 10 | 2 |
|
||
| 8 — US9 | P1 (E2E) | 12 | 3 |
|
||
| 9 — US6 | P2 | 10 | 5 |
|
||
| 10 — US7 | P2 | 12 | 5 |
|
||
| 11 — US8 | P3 | 8 | 4 |
|
||
| 12 — Polish | — | 11 | 3 |
|
||
| **Total** | | **135** | **51 [P]** |
|
||
|
||
## Independent Test Criteria (summary)
|
||
|
||
| Story | Priority | Independent test |
|
||
|---|---|---|
|
||
| US1 | P1 | `create_goal` → row + channel + coordinator exist |
|
||
| US2 | P1 | Coordinator posts tree proposal → approve → tasks materialized atomically |
|
||
| US3 | P1 | `propose_agent` valid + invalid → approval → new agent with hash/parent/seeded rep |
|
||
| US4 | P1 | 100-way concurrent claim → exactly one winner; claim → heartbeat → run → awaiting_verification |
|
||
| US5 | P1 | Each verifier kind (auto/peer/command) drives correct terminal state + reputation row |
|
||
| US6 | P2 | Request resource → set secret → next run sees env var |
|
||
| US7 | P2 | Race two runs at 80 % → exactly one alert; cumulative at 100 % → paused |
|
||
| US8 | P3 | Reputation drop → auto-quarantine + claim rejected; unquarantine restores |
|
||
| US9 | P1 | Full example pipeline produces `report.html` with 6 sections non-empty |
|