Part of #1 (project bootstrap, v0.1). Blocked by #5.
Scope: the agent loop and its two collaborators (router, approver).
Deliverables
core/loop/
Config struct (Provider, MCP, Router, Approver, Logger, MaxSteps)
Run(ctx, cfg, sess, userMessage) (<-chan event.Event, error)
- Behavior:
- Append user message to session
- Optional
Router.Narrow for the turn
- Stream from Provider with narrowed tools
- Forward
TextDelta; on ToolCallRequest → Approver.Approve → MCP dispatch → append result to session → re-stream
- Forward
Finish / Error and exit
- Enforce
MaxSteps cap
- Recover from panics in tool handlers →
event.Error; do not recover panics from Provider/core (spec §15)
- Structured logging via
slog
core/router/
Router interface — Narrow(ctx, userMessage, *mcp.Catalog) ([]mcp.Tool, error)
PassThrough impl (full catalog)
ToolkitClassifier impl — first-pass call against the same Provider asking "which toolkits?", returns those toolkits' tools
- Toolkit definitions and classifier prompt under
docs/prompts/ (version-controlled, reviewable)
core/approval/
Decision enum (Allow, Deny, AllowAll)
Approver interface — Approve(ctx, event.ToolCallRequest) (Decision, error)
AutoAllow (trust MCP-declared permissions)
Interactive (TTY prompt — used by cmd/repl later)
Policy (rule-based: allow datahub_* reads, prompt on trino_execute, deny on datahub_delete)
core/log/
- slog helpers: tool-call log line with server, tool name, hashed argument digest, latency, success/error class
Tests
- Loop tests using
provider.Fake — no live model, no live MCP
- Router tests with both impls
- Approval tests for all three impls
-race clean, -shuffle=on clean
- Coverage contributes to the >80%
core/... gate
Out of scope
- SQL safety middleware (v0.2)
- OpenTelemetry tracing (v0.3)
Branch
feat/bootstrap-loop-router-approval
Spec reference: §6.5, §6.6, §6.7, §11, §12 of the bootstrap spec in #1.
Part of #1 (project bootstrap, v0.1). Blocked by #5.
Scope: the agent loop and its two collaborators (router, approver).
Deliverables
core/loop/Configstruct (Provider, MCP, Router, Approver, Logger, MaxSteps)Run(ctx, cfg, sess, userMessage) (<-chan event.Event, error)Router.Narrowfor the turnTextDelta; onToolCallRequest→Approver.Approve→ MCP dispatch → append result to session → re-streamFinish/Errorand exitMaxStepscapevent.Error; do not recover panics from Provider/core (spec §15)slogcore/router/Routerinterface —Narrow(ctx, userMessage, *mcp.Catalog) ([]mcp.Tool, error)PassThroughimpl (full catalog)ToolkitClassifierimpl — first-pass call against the same Provider asking "which toolkits?", returns those toolkits' toolsdocs/prompts/(version-controlled, reviewable)core/approval/Decisionenum (Allow,Deny,AllowAll)Approverinterface —Approve(ctx, event.ToolCallRequest) (Decision, error)AutoAllow(trust MCP-declared permissions)Interactive(TTY prompt — used bycmd/repllater)Policy(rule-based: allowdatahub_*reads, prompt ontrino_execute, deny ondatahub_delete)core/log/Tests
provider.Fake— no live model, no live MCP-raceclean,-shuffle=oncleancore/...gateOut of scope
Branch
feat/bootstrap-loop-router-approvalSpec reference: §6.5, §6.6, §6.7, §11, §12 of the bootstrap spec in #1.