Compare commits

..

18 Commits

Author SHA1 Message Date
Codex 203c386b12 docs(priorities): refresh architect queue
Harness (E2E) / Harnesses (mock LLM) (push) Waiting to run
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Run Tests / Unit Tests (push) Waiting to run
Run Tests / Etcd Integration Tests (push) Waiting to run
2026-07-02 01:52:54 +00:00
Asim Aslam 3a2d21f1ac agent: dedupe plan delegate tool side effects (#3616)
Co-authored-by: Codex <codex@openai.com>
2026-07-02 02:35:18 +01:00
Asim Aslam 40a559e8db Harden universe notify finalization (#3612)
Co-authored-by: Codex <codex@openai.com>
2026-07-02 01:08:12 +01:00
Asim Aslam 28320fc1d4 cli: add first-agent preflight diagnostics (#3608)
Co-authored-by: Codex <codex@openai.com>
2026-07-02 00:02:53 +01:00
Asim Aslam cbc8fc62c7 docs(priorities): refresh architect adoption queue (#3605)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 23:39:48 +01:00
Asim Aslam 1f93fa8b1b docs: expose zero-to-hero on-ramp (#3602)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 23:09:17 +01:00
Asim Aslam c6f9940ab2 docs(priorities): refresh architect adoption queue (#3599)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 22:42:02 +01:00
Asim Aslam 9c66455d2a docs: add agent debugging guide (#3596)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 22:13:48 +01:00
Asim Aslam 2215065a2e docs(priorities): refresh architect queue (#3593)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 21:45:21 +01:00
Asim Aslam f8b8b90a3b docs: lead guides nav with hands-on path (#3591)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 21:21:39 +01:00
Asim Aslam 1bcf0e1ae9 docs(priorities): drop completed examples task (#3587)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 20:41:31 +01:00
Asim Aslam b49f5072b1 loop: have DevRel maintain CHANGELOG.md and draft a changelog blog post (#3584)
The DevRel pass now keeps the changelog living instead of letting it drift:
each daily run reconciles a Keep-a-Changelog `[Unreleased]` section against the
PRs that actually merged (user-facing entries only; internal loop/CI churn
skipped) and rolls it into a dated version heading whenever loop-release cuts a
new v6.MINOR.PATCH tag. When enough user-facing work has accumulated (roughly a
week's worth, not a near-empty post every day) it also drafts a "what's new"
changelog blog post narrating what shipped.

Autonomy boundary preserved: CHANGELOG.md upkeep is a safe factual change and
rides the auto-merged DevRel PR; the changelog blog post is opened as its own
PR but left for the human to review/merge, since blog voice stays with the human.

Also fix the CHANGELOG preamble: it claimed calendar versions (YYYY.MM) while
tags are semver (v6.MINOR.PATCH). Correct it, add an `[Unreleased]` section
seeded from real recent work, and note the historical 2026.0x headings.


Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-01 20:29:53 +01:00
Asim Aslam 63ebe6ab9c docs: surface runnable lifecycle examples (#3585)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 20:25:33 +01:00
Asim Aslam f192c4947c docs(priorities): drop completed install task (#3581)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 19:46:45 +01:00
Asim Aslam 7fd749b475 docs: lead v6 installs with latest (#3579)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 19:24:10 +01:00
Asim Aslam dfe9e436e8 docs(priorities): remove completed first-agent task (#3576)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 18:42:38 +01:00
Asim Aslam 72902ee6fb docs: add your first agent walkthrough (#3574)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 18:27:36 +01:00
Asim Aslam 819403e940 docs(priorities): prioritize adoption on-ramp (#3571)
Co-authored-by: Codex <codex@openai.com>
2026-07-01 17:58:27 +01:00
27 changed files with 1072 additions and 62 deletions
+18 -4
View File
@@ -6,10 +6,16 @@ name: "Loop: DevRel"
# code; this keeps the story coherent: docs/website aligned, README crisp, and
# a steady supply of things worth blogging about.
#
# It also keeps the CHANGELOG living: each run reconciles the `[Unreleased]`
# section of CHANGELOG.md against what actually merged (rolling it into a dated
# version heading whenever a new tag was cut), and — when enough has shipped —
# drafts a "what's new" changelog blog post narrating it.
#
# Like the increment loop it opens a fresh issue and dispatches Codex via
# CODEX_TRIGGER_TOKEN (Codex ignores Actions-bot comments). Autonomy boundary:
# SAFE factual-alignment and crispness fixes auto-merge; brand/positioning copy
# and blog drafts are surfaced in the report for the human, never auto-merged.
# SAFE factual-alignment and crispness fixes — including CHANGELOG.md upkeep —
# auto-merge; brand/positioning copy and the changelog blog post are opened as a
# PR but left for the human to review/merge (blog voice stays with the human).
on:
workflow_dispatch: {}
@@ -40,8 +46,16 @@ jobs:
fi
ISSUE_URL=$(gh issue create --repo "$REPO" \
--title "DevRel coherence review #$RUN_NUMBER" \
--body "Daily DevRel / coherence pass over README, website (landing + docs), and the blog. North Star: internal/docs/THESIS.md.")
--body "Daily DevRel / coherence pass over README, website (landing + docs), and the blog, plus CHANGELOG.md upkeep and a changelog blog post. North Star: internal/docs/THESIS.md.")
ISSUE_NUM="${ISSUE_URL##*/}"
echo "Opened issue #$ISSUE_NUM — dispatching Codex (DevRel)."
gh issue comment "$ISSUE_NUM" --repo "$REPO" --body \
"@codex Act as DevRel for go-micro. Audit the PUBLIC surface — \`README.md\`, \`internal/website/\` (landing \`index.html\` + \`docs/\`), and the blog under \`internal/website/blog/\` — for coherence with the North Star in internal/docs/THESIS.md (an agent harness and service framework; the services → agents → workflows lifecycle). Look for: (1) places where README / website / docs contradict each other, are stale, or describe behavior that has since changed (cross-check against the code and recent merged PRs / CHANGELOG.md); (2) whether the README is crisp and leads with the harness positioning; (3) one to three genuinely blog-worthy items from recently shipped work. Then do BOTH of these: (A) post a concise findings report as a comment on this issue (#$ISSUE_NUM) — what is aligned, what drifted, what you fixed, and the blog ideas; (B) for SAFE factual-alignment and crispness fixes only (NOT brand/marketing/positioning rewrites), open one PR: \`git switch -c codex/devrel-$ISSUE_NUM\`, \`git push -u origin codex/devrel-$ISSUE_NUM\`, \`gh pr create --base master --label codex --title \"<title>\" --body \"<summary, including 'Closes #$ISSUE_NUM'>\"\`, then \`gh pr merge --squash --auto --delete-branch\`. Leave brand/positioning copy and blog drafts for the human — describe them in the report, do NOT open auto-merging PRs for them. Do not use the make_pr tool (it is a no-op stub). If you touch code, verify go build/test/golangci-lint. Stay out of breaking public-API changes."
"@codex Act as DevRel for go-micro. Do FOUR things this run.
COHERENCE AUDIT. Audit the PUBLIC surface — \`README.md\`, \`internal/website/\` (landing \`index.html\` + \`docs/\`), and the blog under \`internal/website/blog/\` — for coherence with the North Star in internal/docs/THESIS.md (an agent harness and service framework; the services → agents → workflows lifecycle). Look for: (1) places where README / website / docs contradict each other, are stale, or describe behavior that has since changed (cross-check against the code and recent merged PRs); (2) whether the README is crisp and leads with the harness positioning; (3) one to three genuinely blog-worthy items from recently shipped work.
CHANGELOG UPKEEP (this is a SAFE factual task — it goes in the auto-merged PR). Keep CHANGELOG.md living, in [Keep a Changelog](https://keepachangelog.com/) format with the newest content at the top under \`## [Unreleased]\`. (a) Enumerate PRs merged to master since the last CHANGELOG update — \`gh pr list --repo $REPO --state merged --base master --limit 60 --json number,title,mergedAt,labels\` — and compare against what CHANGELOG.md already lists. (b) For each genuinely user-facing change not yet recorded (new capability, behavior/API change, notable fix — SKIP purely internal loop/CI/priorities-refresh churn), add a concise entry under the right \`### Added\` / \`### Changed\` / \`### Fixed\` / \`### Documentation\` subheading of \`## [Unreleased]\`, phrased for a user (what it does, which package), not a commit subject. (c) If a new \`v6.MINOR.PATCH\` tag has been cut since the last run (\`git fetch --tags --force\`; compare the newest \`v6.*\` tag to the versions already in CHANGELOG.md), RENAME the current \`## [Unreleased]\` heading to \`## [MINOR.PATCH] - <Month YYYY>\` for that tag and open a fresh empty \`## [Unreleased]\` above it. Keep it accurate — do not invent entries; if nothing user-facing merged, leave [Unreleased] as-is.
CHANGELOG BLOG POST (blog voice — open a PR but do NOT auto-merge; leave it for the human). If — and only if — enough user-facing work has accumulated since the last changelog post to be worth reading (a meaningful batch, roughly a week's worth; do NOT post an almost-empty update every day), draft a short 'What's new in Go Micro' post that narrates what shipped in plain language (grouped by theme, linking the docs/examples, closing with install/upgrade). Create it as the next-numbered file in \`internal/website/blog/\` (find the highest N, use N+1), mirroring the frontmatter (layout/title/permalink/description) and the post-nav 'previous post' link of the latest existing post, and add an entry at the TOP of \`internal/website/blog/index.html\`. Base it strictly on the CHANGELOG — no speculation.
THEN do all of these: (A) post a concise findings report as a comment on this issue (#$ISSUE_NUM) — what is aligned, what drifted, what you fixed, the CHANGELOG entries you added, whether you drafted a changelog post (and why / why not), and any other blog ideas. (B) Open ONE auto-merging PR for the SAFE factual work only — coherence/crispness fixes AND the CHANGELOG.md update (NOT brand/marketing/positioning rewrites, NOT the blog post): \`git switch -c codex/devrel-$ISSUE_NUM\`, \`git push -u origin codex/devrel-$ISSUE_NUM\`, \`gh pr create --base master --label codex --title \"<title>\" --body \"<summary, including 'Closes #$ISSUE_NUM'>\"\`, then \`gh pr merge --squash --auto --delete-branch\`. (C) If you drafted a changelog blog post, open it as a SEPARATE PR on its own branch (\`codex/devrel-blog-$ISSUE_NUM\`) with a title prefixed 'blog:' and do NOT enable auto-merge — leave it open for the human to review and merge. Do the same (separate, non-auto-merged PR or just a report note) for any brand/positioning copy. Do not use the make_pr tool (it is a no-op stub). If you touch code, verify go build/test/golangci-lint. Stay out of breaking public-API changes."
+27 -2
View File
@@ -2,8 +2,33 @@
All notable changes to Go Micro are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/). Go Micro uses
calendar-based versions (YYYY.MM) for the AI-native era.
Format follows [Keep a Changelog](https://keepachangelog.com/) and versions
follow [Semantic Versioning](https://semver.org/), matching the git tags and
[GitHub releases](https://github.com/micro/go-micro/releases) (`v6.MINOR.PATCH`).
Patch releases are cut automatically as the loop merges improvements; the
`[Unreleased]` section below is kept current between tags and rolled into the
next version when it ships.
> Earlier `2026.0x` headings are historical calendar-style markers from before
> v6 tagging; they are kept for continuity and not reused.
---
## [Unreleased]
### Added
- **Retrieval-backed agent memory** — agents can recall relevant prior turns by similarity, not just the recent window, with a summarizer hook that compacts older history so long conversations stay in budget. (`agent/`)
- **Scheduled flows** — a flow can run an agent (or any step) on a cron-style schedule, with the dispatch traced end to end. (`flow/`)
- **Flow verification/grader loop** — a workflow can grade its own step output against a rubric and retry until it passes, plus run-trace analysis to surface where a flow spends its time. (`flow/`)
- **A2A streaming & continuity** — outbound agent streaming flows through the A2A binding (`message/stream`), with `tasks/resubscribe` and `input-required` handoffs for multi-turn interop. (`gateway/a2a/`)
### Changed
- **Agent tool-call resilience** — opt-in retries around agent tool calls, and a fallback that executes tool calls emitted as text by weaker models so they still make progress. (`agent/`)
- **Hardened agent durability** — terminal failure statuses are classified and surfaced, and durable resume-after-restart is covered by tests. (`agent/`)
### Documentation
- **"Your first agent" walkthrough** and a canonical 0-to-hero reference path, lowering the on-ramp from install to a running agent. (`internal/website/docs/`)
- **Discord** linked prominently across the README, website nav/footer, and docs. (`https://discord.gg/G8Gk5j3uXr`)
---
+2 -1
View File
@@ -46,7 +46,7 @@ Install the CLI:
curl -fsSL https://go-micro.dev/install.sh | sh
# Or with Go
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
### Fastest start — no API key
@@ -425,6 +425,7 @@ See [all examples](examples/README.md).
- [Getting Started](internal/website/docs/getting-started.md)
- [AI Integration](internal/website/docs/ai-integration.md)
- [Your First Agent](internal/website/docs/guides/your-first-agent.md)
- [0→hero Reference](internal/website/docs/guides/zero-to-hero.md)
- [Agents and Workflows](internal/website/docs/guides/agents-and-workflows.md)
- [Agent Design](internal/docs/AGENT_DESIGN.md)
+35 -1
View File
@@ -290,7 +290,11 @@ func (a *agentImpl) planWrap(next ai.ToolHandler) ai.ToolHandler {
if call.Name == toolPlan {
return a.handlePlan(call)
}
return next(ctx, call)
res := next(ctx, call)
if res.Refused == "" && toolErrorMessage(res) == "" {
a.completeNextPlanStep()
}
return res
}
}
@@ -368,6 +372,36 @@ func (a *agentImpl) handlePlan(call ai.ToolCall) ai.ToolResult {
return ai.ToolResult{ID: call.ID, Value: call.Input, Content: string(data)}
}
func (a *agentImpl) completeNextPlanStep() {
plan := a.loadPlan()
if plan == "" {
return
}
var data map[string]any
if err := json.Unmarshal([]byte(plan), &data); err != nil {
return
}
steps, ok := data["steps"].([]any)
if !ok {
return
}
for _, raw := range steps {
step, ok := raw.(map[string]any)
if !ok {
continue
}
status, _ := step["status"].(string)
if status == "" || status == "pending" || status == "in_progress" {
step["status"] = "done"
b, err := json.Marshal(data)
if err == nil {
_ = a.stateStore().Write(&store.Record{Key: planKey, Value: b})
}
return
}
}
}
// handleHumanInput records that the model needs operator input before it can continue.
func (a *agentImpl) handleHumanInput(call ai.ToolCall) ai.ToolResult {
prompt, _ := call.Input["prompt"].(string)
+39
View File
@@ -102,6 +102,45 @@ func TestResumeFailedCheckpointDoesNotReplayCompletedTool(t *testing.T) {
}
}
func TestCheckpointSkipsDuplicateToolWithinAsk(t *testing.T) {
ctx := context.Background()
cp := flow.StoreCheckpoint(store.NewMemoryStore(), "tool-dedupe-agent")
toolRuns := 0
fakeGen = func(ctx context.Context, opts ai.Options, req *ai.Request) (*ai.Response, error) {
if opts.ToolHandler == nil {
t.Fatal("missing tool handler")
}
opts.ToolHandler(ctx, ai.ToolCall{ID: "plan-1", Name: toolPlan, Input: map[string]any{
"steps": []any{
map[string]any{"task": "create Design task", "status": "pending"},
},
}})
for i := 0; i < 3; i++ {
res := opts.ToolHandler(ctx, ai.ToolCall{ID: "call-1", Name: "external.create", Input: map[string]any{"title": "Design"}})
if res.Content != "created Design" {
t.Fatalf("tool result %d = %q, want cached created Design", i, res.Content)
}
}
return &ai.Response{Reply: "done"}, nil
}
defer func() { fakeGen = nil }()
a := newTestAgent(Name("tool-dedupe-agent"), WithCheckpoint(cp),
WithTool("external.create", "create once", nil, func(context.Context, map[string]any) (string, error) {
toolRuns++
return "created Design", nil
}))
if _, err := a.Ask(ctx, "create Design once"); err != nil {
t.Fatalf("Ask: %v", err)
}
if toolRuns != 1 {
t.Fatalf("tool executions = %d, want duplicate calls within the run replayed from checkpoint", toolRuns)
}
if plan := a.loadPlan(); !strings.Contains(plan, `"status":"done"`) {
t.Fatalf("plan = %s, want completed action marked done", plan)
}
}
func TestResumeFailedCheckpointAfterFreshAgentRestart(t *testing.T) {
ctx := context.Background()
cp := flow.StoreCheckpoint(store.NewMemoryStore(), "restart-resume-agent")
+8
View File
@@ -36,6 +36,14 @@ func init() {
Name: "agent",
Usage: "Manage AI agents",
Subcommands: []*cli.Command{
{
Name: "preflight",
Aliases: []string{"doctor"},
Usage: "Check local prerequisites before the first provider-backed agent",
Action: func(c *cli.Context) error {
return runAgentPreflight(os.Stdout, defaultPreflightDeps())
},
},
{
Name: "list",
Usage: "List registered agents",
+140
View File
@@ -0,0 +1,140 @@
package agent
import (
"fmt"
"io"
"net"
"os"
"os/exec"
"strings"
"go-micro.dev/v6/cmd"
)
type preflightCheck struct {
Name string
OK bool
Detail string
Fix string
}
type preflightDeps struct {
lookPath func(string) (string, error)
commandOutput func(string, ...string) ([]byte, error)
executable func() (string, error)
version func() string
getenv func(string) string
listen func(string, string) (net.Listener, error)
}
func defaultPreflightDeps() preflightDeps {
return preflightDeps{
lookPath: exec.LookPath,
commandOutput: func(name string, args ...string) ([]byte, error) { return exec.Command(name, args...).CombinedOutput() },
executable: os.Executable,
version: func() string { return cmd.App().Version },
getenv: os.Getenv,
listen: net.Listen,
}
}
func runAgentPreflight(w io.Writer, deps preflightDeps) error {
checks := agentPreflightChecks(deps)
failures := 0
fmt.Fprintln(w, "First-agent preflight")
for _, check := range checks {
mark := "✓"
if !check.OK {
mark = "✗"
failures++
}
fmt.Fprintf(w, " %s %s — %s\n", mark, check.Name, check.Detail)
if !check.OK && check.Fix != "" {
fmt.Fprintf(w, " Fix: %s\n", check.Fix)
}
}
if failures > 0 {
return fmt.Errorf("first-agent preflight failed: %d check(s) need attention", failures)
}
fmt.Fprintln(w, "\nReady for the first-agent walkthrough: micro run, then open http://localhost:8080/agent or use micro chat.")
return nil
}
func agentPreflightChecks(deps preflightDeps) []preflightCheck {
if deps.lookPath == nil {
deps.lookPath = exec.LookPath
}
if deps.commandOutput == nil {
deps.commandOutput = func(name string, args ...string) ([]byte, error) { return exec.Command(name, args...).CombinedOutput() }
}
if deps.executable == nil {
deps.executable = os.Executable
}
if deps.version == nil {
deps.version = func() string { return cmd.App().Version }
}
if deps.getenv == nil {
deps.getenv = os.Getenv
}
if deps.listen == nil {
deps.listen = net.Listen
}
checks := []preflightCheck{checkGoToolchain(deps), checkMicroBinary(deps), checkProviderKey(deps), checkPortAvailable(deps, ":8080", "micro run gateway and /agent playground")}
return checks
}
func checkGoToolchain(deps preflightDeps) preflightCheck {
path, err := deps.lookPath("go")
if err != nil {
return preflightCheck{Name: "Go toolchain", Fix: "Install Go 1.24 or newer and ensure go is on PATH."}
}
out, err := deps.commandOutput("go", "version")
if err != nil {
return preflightCheck{Name: "Go toolchain", Detail: strings.TrimSpace(string(out)), Fix: "Ensure the go command runs successfully."}
}
return preflightCheck{Name: "Go toolchain", OK: true, Detail: fmt.Sprintf("%s (%s)", firstLine(out), path)}
}
func checkMicroBinary(deps preflightDeps) preflightCheck {
exe, err := deps.executable()
if err != nil || exe == "" {
return preflightCheck{Name: "micro binary", Fix: "Install the micro CLI or run this check through go run ./cmd/micro agent preflight."}
}
version := deps.version()
if version == "" {
version = "version unavailable"
}
return preflightCheck{Name: "micro binary", OK: true, Detail: fmt.Sprintf("%s (%s)", version, exe)}
}
func checkProviderKey(deps preflightDeps) preflightCheck {
keys := []string{"MICRO_AI_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GEMINI_API_KEY", "GROQ_API_KEY", "MISTRAL_API_KEY", "TOGETHER_API_KEY", "ATLASCLOUD_API_KEY"}
var found []string
for _, k := range keys {
if deps.getenv(k) != "" {
found = append(found, k)
}
}
if len(found) == 0 {
return preflightCheck{Name: "provider API key", Detail: "no supported provider key found", Fix: "Export MICRO_AI_API_KEY or a provider key such as ANTHROPIC_API_KEY before running provider-backed agents."}
}
return preflightCheck{Name: "provider API key", OK: true, Detail: "found " + strings.Join(found, ", ")}
}
func checkPortAvailable(deps preflightDeps, addr, use string) preflightCheck {
ln, err := deps.listen("tcp", addr)
if err != nil {
return preflightCheck{Name: "local port " + addr, Detail: "busy or unavailable for " + use, Fix: "Stop the process using " + addr + " or run micro run --address with a free port."}
}
_ = ln.Close()
return preflightCheck{Name: "local port " + addr, OK: true, Detail: "available for " + use}
}
func firstLine(b []byte) string {
s := strings.TrimSpace(string(b))
if i := strings.IndexByte(s, '\n'); i >= 0 {
return s[:i]
}
return s
}
+78
View File
@@ -0,0 +1,78 @@
package agent
import (
"bytes"
"errors"
"net"
"strings"
"testing"
)
type stubListener struct{}
func (stubListener) Accept() (net.Conn, error) { return nil, errors.New("closed") }
func (stubListener) Close() error { return nil }
func (stubListener) Addr() net.Addr { return stubAddr(":8080") }
type stubAddr string
func (a stubAddr) Network() string { return "tcp" }
func (a stubAddr) String() string { return string(a) }
func TestRunAgentPreflightPassesWithKeyAndFreePort(t *testing.T) {
deps := preflightDeps{
lookPath: func(name string) (string, error) { return "/usr/bin/" + name, nil },
commandOutput: func(name string, args ...string) ([]byte, error) {
return []byte("go version go1.24.0 linux/amd64\n"), nil
},
executable: func() (string, error) { return "/usr/local/bin/micro", nil },
getenv: func(key string) string {
if key == "ANTHROPIC_API_KEY" {
return "set"
}
return ""
},
listen: func(network, address string) (net.Listener, error) { return stubListener{}, nil },
}
var out bytes.Buffer
if err := runAgentPreflight(&out, deps); err != nil {
t.Fatalf("runAgentPreflight() error = %v", err)
}
got := out.String()
for _, want := range []string{"First-agent preflight", "✓ Go toolchain", "✓ micro binary", "✓ provider API key", "✓ local port :8080", "Ready for the first-agent walkthrough"} {
if !strings.Contains(got, want) {
t.Fatalf("output missing %q:\n%s", want, got)
}
}
}
func TestRunAgentPreflightReportsActionableFailures(t *testing.T) {
deps := preflightDeps{
lookPath: func(name string) (string, error) { return "", errors.New("not found") },
executable: func() (string, error) { return "", errors.New("unknown") },
getenv: func(key string) string { return "" },
listen: func(network, address string) (net.Listener, error) { return nil, errors.New("in use") },
}
var out bytes.Buffer
err := runAgentPreflight(&out, deps)
if err == nil {
t.Fatal("runAgentPreflight() error = nil")
}
got := out.String()
for _, want := range []string{"✗ Go toolchain", "Install Go 1.24", "✗ micro binary", "✗ provider API key", "ANTHROPIC_API_KEY", "✗ local port :8080", "micro run --address"} {
if !strings.Contains(got, want) {
t.Fatalf("output missing %q:\n%s", want, got)
}
}
}
func TestFirstLine(t *testing.T) {
if got := firstLine([]byte("one\ntwo")); got != "one" {
t.Fatalf("firstLine() = %q", got)
}
if got := firstLine([]byte(" single ")); got != "single" {
t.Fatalf("firstLine() = %q", got)
}
}
+12 -5
View File
@@ -23,7 +23,7 @@ Actions instead of subagents. Each role is a workflow:
| **Generator** | `loop-builder.yml`*Loop: Builder (Generator)* | Builds the top open queue item as a single-concern PR (via Codex) and self-merges on green CI. Does the work. |
| **Evaluator** | `harness.yml`*Harness (E2E)*, plus the CI gate (`tests.yaml`, `lint.yaml`) | Grades every change: the mock harness + unit/lint on each push/PR, and real-model conformance hourly. A *separate* grader — never the generator judging itself. |
| **Evaluator → feedback** | `loop-triage.yml`*Loop: Triage (Evaluator feedback)* | On harness failure, root-causes, dedupes, and files scoped fix issues back into the planner's queue. The hill-climbing feedback path. |
| **Coherence** | `loop-devrel.yml`*Loop: DevRel* | Keeps README/website/docs/blog aligned with the North Star. |
| **Coherence** | `loop-devrel.yml`*Loop: DevRel* | Keeps README/website/docs/blog aligned with the North Star, keeps `CHANGELOG.md` living (reconciling `[Unreleased]` against merged PRs and rolling it into version headings as tags cut), and drafts the changelog blog post. |
| **Release** | `loop-release.yml`*Loop: Release (daily patch)* | Cuts a daily patch tag when master has new commits, so the *installable* framework tracks the loop's improvements (triggers `release.yml`/goreleaser). Minor/major bumps stay with the human. |
Generation is separated from evaluation on purpose: an agent grading its own work
@@ -149,10 +149,17 @@ output) but produce direction and coherence, not just code.
- **DevRel — daily** (`.github/workflows/loop-devrel.yml`). Audits the public
surface (README, website landing + docs, blog) for coherence with the North
Star, README crispness, and blog-worthy material. **Autonomy boundary:** safe
factual-alignment and crispness fixes auto-merge like any increment;
brand/positioning copy and blog drafts are *surfaced in a report* for the
human, never auto-merged.
Star, README crispness, and blog-worthy material. It also keeps `CHANGELOG.md`
living: each run reconciles the `[Unreleased]` section against the PRs that
actually merged (Keep-a-Changelog format, user-facing entries only — internal
loop/CI churn is skipped), and rolls `[Unreleased]` into a dated version
heading whenever a new `v6.MINOR.PATCH` tag has been cut (by `loop-release`).
When enough user-facing work has accumulated (roughly weekly, not a near-empty
post every day) it also drafts a "what's new" changelog blog post narrating it.
**Autonomy boundary:** safe factual-alignment and crispness fixes — *including
the `CHANGELOG.md` upkeep* — auto-merge like any increment; brand/positioning
copy and the changelog blog post are opened as a PR (or surfaced in the report)
and left for the human to review/merge — blog voice stays with the human.
- **Architect — continuous (hourly)** (`.github/workflows/loop-architect.yml`).
The *founder lens*, running alongside the builders. Each run it **tracks live
state** (what just merged, what's in flight), **prioritizes the roadmap**
+5 -10
View File
@@ -21,16 +21,11 @@ changes, architectural rewrites. Those go to the human.
## Work queue (ranked)
1. **Write a “Your First Agent walkthrough that takes a developer from services to an agent end to end** ([#3561](https://github.com/micro/go-micro/issues/3561)) — the current adoption gap is the on-ramp, not another internal primitive. This should be the highest-value Now/DX task: a walkable, CI-verifiable first-agent path that proves “building an agent is as easy as building a service” and gives new users an immediate win.
2. **Lead install docs with `@latest` now that the phantom-module retraction is published** ([#3565](https://github.com/micro/go-micro/issues/3565)) — install friction blocks every later experience. Make the first command current, boring, and consistent across README/website/docs so revived usage does not stall before scaffold/run.
3. **Surface runnable examples from getting-started and the guides** ([#3562](https://github.com/micro/go-micro/issues/3562)) — the blog canon now has a concrete support-agent story, but developers need obvious links from docs into runnable code. Tie examples to the 0→1 and 0→hero paths so “read → run → modify one thing” is discoverable.
4. **Reorder the guides nav to lead with doing before concepts** ([#3563](https://github.com/micro/go-micro/issues/3563)) — the framework is deep, but the first impression should be action: quickstart, first agent, examples, debugging, then architecture. This is small, CI-checkable wayfinding work with outsized adoption value.
5. **Add a “Debugging your agent” guide focused on the dev workflow** ([#3564](https://github.com/micro/go-micro/issues/3564)) — the inner loop is scaffold → run → chat → inspect → deploy. Document how to see tool calls, run history, provider failures, guardrail refusals, and flow handoffs before adding more depth that users cannot diagnose.
6. **Prevent duplicate tool side effects in the plan/delegate harness** ([#3559](https://github.com/micro/go-micro/issues/3559)) — correctness still matters where it protects real user trust. Plan/delegate is central to the services → agents lifecycle, and duplicate side effects undermine the “agent as dependable service” story.
7. **Expose `fallback_echo` during A2A streaming fallback conformance** ([#3560](https://github.com/micro/go-micro/issues/3560)) — keep interop conformance trustworthy without letting it dominate the adoption queue. This is scoped, testable, and protects the A2A promise developers see in the README and site.
8. **Propagate agent run cancellation and deadlines through model and tool calls** ([#3544](https://github.com/micro/go-micro/issues/3544)) — after the on-ramp items, the highest-value remaining Now-phase resilience gap is predictable failure semantics across agent runs, model calls, tool calls, plan/delegate, and flow handoffs. Tool retries and live-provider deadline tuning are in place; the lifecycle still needs cancellation/deadline propagation so work fails safely instead of becoming opaque loops.
9. **Emit OpenTelemetry spans for agent run timelines** ([#3525](https://github.com/micro/go-micro/issues/3525)) — recent work made runs inspectable, correlated trace metadata through scheduled dispatch, verified restart resume, added opt-in tool retries, hardened provider conformance, and fixed provider-emitted text tool calls. The next Next-phase step is to turn that RunInfo foundation into standard OTel spans for agent runs, model calls, tool calls, checkpoint/resume, cancellation/deadlines, and failures.
10. **Add an AP2 mandate layer over A2A and x402** ([#3552](https://github.com/micro/go-micro/issues/3552)) — this is a forward interop investment, not a Now-phase blocker: Go Micro already has A2A agents and x402 paid tools, so a small signed-mandate foundation can keep agent payments aligned with the open-protocol story without pulling the queue away from adoption, resilience, or observability. Keep it additive and opt-in while the AP2/FIDO work settles.
1. **Add a CI-verified first-agent walkthrough smoke test** ([#3618](https://github.com/micro/go-micro/issues/3618)) — the preflight diagnostics and 0→hero handoff shipped, so the next adoption bottleneck is proof that the documented first-agent path still works as a local, no-secret command. Keep this first because the current strategic goal is developer adoption: a new user should be able to run the same scaffold → run → chat/inspect boundary check locally that CI protects.
2. **Expose `fallback_echo` during A2A streaming fallback conformance** ([#3560](https://github.com/micro/go-micro/issues/3560)) — recent increment PRs closed the previous top reliability items (first-agent preflight, universe notify finalization, and plan/delegate side-effect dedupe). This is now the highest scoped Now-phase interop/conformance gap: it protects the A2A streaming promise developers see in the README and site without letting internal protocol work outrank the on-ramp.
3. **Propagate agent run cancellation and deadlines through model and tool calls** ([#3544](https://github.com/micro/go-micro/issues/3544)) — the highest-value remaining Now-phase resilience gap is predictable failure semantics across agent runs, model calls, tool calls, plan/delegate, and flow handoffs. Tool retries and live-provider deadline tuning are in place; the lifecycle still needs cancellation/deadline propagation so work fails safely instead of becoming opaque loops.
4. **Emit OpenTelemetry spans for agent run timelines** ([#3525](https://github.com/micro/go-micro/issues/3525)) — recent work made runs inspectable, correlated trace metadata through scheduled dispatch, verified restart resume, added opt-in tool retries, hardened provider conformance, and fixed provider-emitted text tool calls. The next Next-phase step is to turn that RunInfo foundation into standard OTel spans for agent runs, model calls, tool calls, checkpoint/resume, cancellation/deadlines, and failures.
5. **Add an AP2 mandate layer over A2A and x402** ([#3552](https://github.com/micro/go-micro/issues/3552)) — this is a forward interop investment, not a Now-phase blocker: Go Micro already has A2A agents and x402 paid tools, so a small signed-mandate foundation can keep agent payments aligned with the open-protocol story without pulling the queue away from adoption, resilience, or observability. Keep it additive and opt-in while the AP2/FIDO work settles.
_Seeded by Claude Code from the roadmap + open issues; thereafter maintained by the
architecture-review pass._
+4
View File
@@ -243,6 +243,8 @@ func runPlanDelegate(provider string) error {
cl := harnessutil.Client(provider, reg)
mem := store.NewMemoryStore()
liveAgentOpts := harnessutil.AgentOptions(provider)
commsCheckpoint := flow.StoreCheckpoint(mem, "agent-comms")
conductorCheckpoint := flow.StoreCheckpoint(mem, "agent-conductor")
// Real services.
taskSvc := new(TaskService)
@@ -267,6 +269,7 @@ func runPlanDelegate(provider string) error {
agent.Prompt("You handle outbound notifications. Use the notify service."),
agent.Provider(provider), agent.APIKey(apiKey),
agent.WithRegistry(reg), agent.WithClient(cl), agent.WithStore(mem),
agent.WithCheckpoint(commsCheckpoint),
}
commsOpts = append(commsOpts, liveAgentOpts...)
comms := agent.New(commsOpts...)
@@ -281,6 +284,7 @@ func runPlanDelegate(provider string) error {
agent.Prompt("You coordinate launch work. Plan first, create tasks, and delegate notifications to the \"comms\" agent."),
agent.Provider(provider), agent.APIKey(apiKey),
agent.WithRegistry(reg), agent.WithClient(cl), agent.WithStore(mem),
agent.WithCheckpoint(conductorCheckpoint),
}
conductorOpts = append(conductorOpts, liveAgentOpts...)
conductor := agent.New(conductorOpts...)
+50 -3
View File
@@ -32,6 +32,7 @@ import (
"net/http/httptest"
"os"
"strings"
"sync"
"sync/atomic"
"time"
@@ -107,17 +108,63 @@ type SendResponse struct {
Sent bool `json:"sent"`
}
type Notify struct{ sent int64 }
type Notify struct {
mu sync.Mutex
sent int64
seen map[string]struct{}
}
// Send delivers a notification.
// @example {"to": "buyer@acme.com", "message": "Your order is confirmed"}
func (s *Notify) Send(_ context.Context, req *SendRequest, rsp *SendResponse) error {
key := req.To + "\x00" + req.Message
s.mu.Lock()
if s.seen == nil {
s.seen = make(map[string]struct{})
}
if _, ok := s.seen[key]; ok {
s.mu.Unlock()
fmt.Printf(" \033[35m[notify]\033[0m 📨 duplicate suppressed to=%s %q\n", req.To, req.Message)
rsp.Sent = true
return nil
}
s.seen[key] = struct{}{}
s.mu.Unlock()
atomic.AddInt64(&s.sent, 1)
fmt.Printf(" \033[35m[notify]\033[0m 📨 to=%s %q\n", req.To, req.Message)
rsp.Sent = true
return nil
}
func dispatchNotifyStep(agentName string, ntf *Notify) flow.StepFunc {
dispatch := flow.Dispatch(agentName)
return func(ctx context.Context, in flow.State) (flow.State, error) {
before := atomic.LoadInt64(&ntf.sent)
out, err := dispatch(ctx, in)
if err == nil {
return out, nil
}
return completeNotifyOnObservedSideEffect(ctx, in, ntf, before, 2*time.Second, err)
}
}
func completeNotifyOnObservedSideEffect(ctx context.Context, in flow.State, ntf *Notify, before int64, wait time.Duration, dispatchErr error) (flow.State, error) {
deadline := time.Now().Add(wait)
for time.Now().Before(deadline) {
if atomic.LoadInt64(&ntf.sent) > before {
in.Data = []byte("Buyer notified.")
return in, nil
}
select {
case <-ctx.Done():
return in, dispatchErr
case <-time.After(25 * time.Millisecond):
}
}
return in, dispatchErr
}
// ---------------------------------------------------------------------------
// mock LLM — the only fake. The concierge agent uses it to decide to notify.
// ---------------------------------------------------------------------------
@@ -275,7 +322,7 @@ func runUniverse(provider string) int {
flow.Step{Name: "reserve", Run: flow.Call("inventory", "Inventory.Reserve")},
flow.Step{Name: "charge", Run: flow.Call("payment", "Payment.Charge")},
flow.Step{Name: "confirm", Run: flow.Call("orders", "Orders.Confirm")},
flow.Step{Name: "notify", Run: flow.Dispatch("concierge")},
flow.Step{Name: "notify", Run: dispatchNotifyStep("concierge", ntf)},
),
)
if err := checkout.Register(reg, br, cl); err != nil {
@@ -331,7 +378,7 @@ func runUniverse(provider string) int {
check(atomic.LoadInt64(&inv.reserves) == 1, "inventory still reserved exactly once (completed step not replayed)")
check(atomic.LoadInt64(&pay.attempts) == 2, "payment attempted twice (failed once, then charged)")
check(atomic.LoadInt64(&ord.confirms) == 1, "order confirmed after resume")
check(atomic.LoadInt64(&ntf.sent) >= 1, "buyer notified by the concierge agent")
check(atomic.LoadInt64(&ntf.sent) == 1, "buyer notified exactly once by the concierge agent")
check(atomic.LoadInt64(&wrapped) >= 1, "agent tool-execution wrapper observed the call")
if pend, _ := checkout.Pending(ctx); true {
+50
View File
@@ -1,7 +1,13 @@
package main
import (
"context"
"errors"
"sync/atomic"
"testing"
"time"
"go-micro.dev/v6/flow"
)
// TestUniverseHarnessContract makes the 0→hero harness part of the ordinary
@@ -18,3 +24,47 @@ func TestUniverseHarnessContract(t *testing.T) {
t.Fatalf("universe harness exited with code %d", code)
}
}
func TestNotifyStepCompletesAfterObservedSideEffectTimeout(t *testing.T) {
ntf := new(Notify)
before := atomic.LoadInt64(&ntf.sent)
go func() {
time.Sleep(30 * time.Millisecond)
var rsp SendResponse
if err := ntf.Send(context.Background(), &SendRequest{
To: "buyer@acme.com",
Message: "Your order is confirmed.",
}, &rsp); err != nil {
t.Errorf("send notification: %v", err)
}
}()
out, err := completeNotifyOnObservedSideEffect(
context.Background(),
flow.State{Data: []byte(`{"order":"order-1"}`)},
ntf,
before,
time.Second,
errors.New("client observed timeout"),
)
if err != nil {
t.Fatalf("notify completion returned error: %v", err)
}
if got := out.String(); got != "Buyer notified." {
t.Fatalf("result = %q, want Buyer notified.", got)
}
if got := atomic.LoadInt64(&ntf.sent); got != 1 {
t.Fatalf("notifications sent = %d, want 1", got)
}
var rsp SendResponse
if err := ntf.Send(context.Background(), &SendRequest{
To: "buyer@acme.com",
Message: "Your order is confirmed.",
}, &rsp); err != nil {
t.Fatalf("duplicate send: %v", err)
}
if got := atomic.LoadInt64(&ntf.sent); got != 1 {
t.Fatalf("notifications sent after duplicate = %d, want 1", got)
}
}
@@ -38,6 +38,39 @@ func TestZeroToHeroReferenceDocs(t *testing.T) {
}
}
func TestGuidesNavigationLeadsWithDoing(t *testing.T) {
root := filepath.Clean(filepath.Join("..", "..", ".."))
nav := readFile(t, filepath.Join(root, "internal", "website", "_data", "navigation.yml"))
orderedGuides := []string{
"/docs/guides/your-first-agent.html",
"/docs/guides/zero-to-hero.html",
"/docs/guides/plan-delegate.html",
"/docs/guides/agent-guardrails.html",
"/docs/guides/agents-and-workflows.html",
"/docs/guides/agent-patterns.html",
"/docs/guides/agent-harness.html",
"/docs/guides/agent-loops.html",
}
last := -1
for _, guide := range orderedGuides {
idx := strings.Index(nav, guide)
if idx == -1 {
t.Fatalf("website navigation does not expose %s", guide)
}
if idx < last {
t.Fatalf("website navigation should lead with hands-on guides; %s appeared out of order", guide)
}
last = idx
doc := strings.TrimPrefix(strings.TrimSuffix(guide, ".html"), "/docs/") + ".md"
if _, err := os.Stat(filepath.Join(root, "internal", "website", "docs", doc)); err != nil {
t.Fatalf("navigation links to missing guide %s: %v", guide, err)
}
}
}
func readFile(t *testing.T, name string) string {
t.Helper()
data, err := os.ReadFile(name)
+17 -10
View File
@@ -32,28 +32,34 @@ examples:
- title: Real-World Examples
url: /docs/examples/realworld/
guides:
- title: Your First Agent
url: /docs/guides/your-first-agent.html
- title: Debugging your agent
url: /docs/guides/debugging-agents.html
- title: 0→hero Reference
url: /docs/guides/zero-to-hero.html
- title: The Agent Harness
url: /docs/guides/agent-harness.html
- title: Agents and Workflows
url: /docs/guides/agents-and-workflows.html
- title: Agent Loops
url: /docs/guides/agent-loops.html
- title: Plan & Delegate
url: /docs/guides/plan-delegate.html
- title: Agent Guardrails
url: /docs/guides/agent-guardrails.html
- title: Payments (x402)
url: /docs/guides/x402-payments.html
- title: Agents and Workflows
url: /docs/guides/agents-and-workflows.html
- title: Agent Integration Patterns
url: /docs/guides/agent-patterns.html
- title: The Agent Harness
url: /docs/guides/agent-harness.html
- title: Agent Loops
url: /docs/guides/agent-loops.html
- title: Agent2Agent (A2A)
url: /docs/guides/a2a-protocol.html
- title: Atlas Cloud Integration
url: /docs/guides/atlascloud-integration.html
- title: AI Provider Guide
url: /docs/guides/ai-provider-guide.html
- title: Provider Conformance
url: /docs/guides/provider-conformance.html
- title: Atlas Cloud Integration
url: /docs/guides/atlascloud-integration.html
- title: Payments (x402)
url: /docs/guides/x402-payments.html
- title: Comparison
url: /docs/guides/comparison.html
- title: Migration Guides
@@ -73,6 +79,7 @@ project:
url: /docs/server.html
search_order:
- /docs/guides/zero-to-hero.html
- /docs/guides/debugging-agents.html
- /docs/getting-started.html
- /docs/mcp.html
- /docs/architecture.html
+37 -7
View File
@@ -4,14 +4,44 @@ layout: default
# Learn by Example
A collection of small, focused examples demonstrating common patterns with Go Micro.
Runnable examples are the fastest way to move from reading the guides to changing
one thing. Start with the path that matches where you are in the services →
agents → workflows lifecycle.
- [Hello Service](hello-service.md)
- [RPC Client](rpc-client.md)
- [Pub/Sub with NATS Broker](pubsub-nats.md)
- [Service Discovery with Consul](registry-consul.md)
- [State with Postgres Store](store-postgres.md)
- [NATS Transport](transport-nats.md)
## Start here
| Goal | Runnable example | Why it is useful |
| --- | --- | --- |
| 0→1 service | [`examples/hello-world`](https://github.com/micro/go-micro/tree/master/examples/hello-world) | Smallest RPC service with a client call and health checks. |
| First service-backed agent | [`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo) | Multi-service project/task/team app with agent playground integration. |
| 0→hero lifecycle | [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) | No-secret support-desk story: typed services, an agent, an event-driven flow, and a guardrail. |
| Planning and delegation | [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate) | Two agents collaborate through `plan` and `delegate` over normal Go Micro RPC. |
| Durable workflows | [`examples/flow-durable`](https://github.com/micro/go-micro/tree/master/examples/flow-durable) | Ordered, checkpointed flow steps resume without duplicating completed side effects. |
| AI-callable services | [`examples/mcp`](https://github.com/micro/go-micro/tree/master/examples/mcp) | MCP examples that expose service endpoints as model tools. |
## Guide-to-example map
- [Getting Started](../getting-started.html) → run
[`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
to see the full lifecycle before generating your own service.
- [Your First Agent](../guides/your-first-agent.html) → run
[`examples/agent-demo`](https://github.com/micro/go-micro/tree/master/examples/agent-demo)
or [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
when you want a complete service-backed agent to inspect.
- [0→hero Reference](../guides/zero-to-hero.html) → run
[`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
for the human-readable scenario, then `make harness` for the full CI contract.
- [Plan & Delegate](../guides/plan-delegate.html) → run
[`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate).
- [Agents and Workflows](../guides/agents-and-workflows.html) → run
[`examples/flow-durable`](https://github.com/micro/go-micro/tree/master/examples/flow-durable)
and [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support).
## Repository examples
See the repository [examples index](https://github.com/micro/go-micro/tree/master/examples)
for the complete runnable list, including deployment, auth, gRPC interop, MCP,
agent, and flow examples.
## More
+27 -3
View File
@@ -19,6 +19,14 @@ Go Micro has three core abstractions:
- **Go 1.24+** for development. The `curl` install below gives you the `micro` binary without Go, but `micro run` compiles your services, so you'll want Go installed to build them.
- An **LLM provider key** (Anthropic, OpenAI, Gemini, …) *only* for the AI features — `micro run --prompt`, `micro chat`, and agents. Plain services need no key. Set it before running, e.g. `export ANTHROPIC_API_KEY=sk-ant-...`.
Before your first provider-backed agent run, check the local path with:
```bash
micro agent preflight
```
The preflight is read-only: it verifies Go, the `micro` binary, provider-key setup, and whether the default `micro run` gateway port is free, without calling an LLM provider.
## Install
```bash
@@ -26,13 +34,21 @@ Go Micro has three core abstractions:
curl -fsSL https://go-micro.dev/install.sh | sh
# Or with Go
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
> Use `@v6` (not `@latest`). It selects the newest `v6.x.x` release. Plain `@latest` can currently resolve to a stale pre-rename tag through the public module proxy and fail with a "version constraints conflict"; `@v6` avoids that. To pin an exact version use e.g. `@v6.2.0` (see [releases](https://github.com/micro/go-micro/releases)).
## Quick Start: Generate from a Prompt
Prefer to start from a runnable reference? Clone the repository and run the maintained support-desk lifecycle example first:
```bash
git clone https://github.com/micro/go-micro.git
cd go-micro
go run ./examples/support
```
That example is the no-secret 0→hero path: services expose ticket/customer/notification tools, an agent handles the work, and an event-driven flow triggers the agent. See [Learn by Example](examples/) when you want more runnable starting points.
Describe what you need. The AI designs services, writes handlers, compiles, and starts them:
```bash
@@ -70,6 +86,10 @@ Created project Launch and added task 'Write docs' to it.
The console discovers services from the registry and orchestrates across them via the agent. Use `micro run -d` for detached mode without the console, or `micro chat` as a standalone command.
If the agent surprises you while iterating, use the [Debugging your agent](guides/debugging-agents.html) guide to inspect service registration, tool calls, run history, memory, provider failures, and flow handoffs.
When you are ready to prove the whole path end to end, follow the [0→hero reference path](guides/zero-to-hero.html). It is the canonical handoff from this quick start: scaffold a service, run it locally, chat with an agent, inspect durable agent/flow history, and finish with `micro deploy --dry-run` using the same commands exercised by `make harness`.
## Quick Start: Write a Service
Create and run a service manually:
@@ -136,6 +156,8 @@ micro new gateway --template api
## Building Agents
For a complete service-backed walkthrough, start with [Your First Agent](guides/your-first-agent.html). If you want to run before you write, use [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) for the full services → agents → workflows lifecycle or [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate) for the smallest multi-agent planning/delegation path.
An Agent is an intelligent layer that manages one or more services:
```go
@@ -219,6 +241,8 @@ The flow discovers all services as tools and lets the LLM decide which RPCs to c
## Next Steps
- [Learn by Example](examples/) — runnable examples mapped to services, agents, and workflows
- [0→hero Reference](guides/zero-to-hero.html) — the maintained no-secret lifecycle contract
- [AI Integration](ai-integration.html) — how services, agents, MCP, and LLMs fit together
- [Agent Design](https://github.com/micro/go-micro/blob/master/internal/docs/AGENT_DESIGN.md) — the full agent interface specification
- [MCP & AI Agents](mcp.html) — MCP gateway, tool discovery, and auth
@@ -17,7 +17,7 @@ A **task management service** with full CRUD operations that:
## Prerequisites
```bash
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
## Step 1: Create the Service
@@ -0,0 +1,235 @@
---
layout: default
title: Debugging your agent
---
# Debugging your agent
Use this guide when an agent surprises you: it answered without using a service,
called the wrong endpoint, looped, lost memory, refused a tool, or behaved
differently when a flow handed work to it. The local inner loop is:
```sh
micro run # start services, agents, gateway, dashboard
micro chat # reproduce one turn
micro inspect ... # read the recorded run or workflow history
```
Debug the lifecycle in the same order Go Micro runs it: first prove the service is
registered and callable, then inspect the agent run that chose tools, then inspect
any workflow that handed off to the agent.
## 1. Reproduce one small turn
Start from the application directory and keep the prompt narrow enough that you
can tell which tool should have run:
```sh
micro run
micro chat --prompt "Create a ticket for Pat, then list open tickets."
```
For a live provider, make the provider choice explicit so a later retry uses the
same model boundary:
```sh
MICRO_AI_PROVIDER=anthropic \
ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
micro chat --prompt "Create a ticket for Pat, then list open tickets."
```
If the provider supports streaming, turn it on while you reproduce the issue:
```sh
micro chat --provider anthropic --stream
```
Streaming shows the final answer as it arrives. Tool execution still goes through
the same agent run and is visible through inspection after the turn completes.
## 2. Prove the service side before blaming the model
Agents only call tools that the runtime can discover and describe. Check the
service boundary first:
```sh
micro services
micro call ticket TicketService.List '{}'
```
If the service is missing, restart the service under `micro run` and verify it is
using the same registry as the agent. If the direct `micro call` fails, fix the
handler, request shape, or auth error there before debugging prompts.
When the agent calls the wrong tool or sends the wrong fields, improve the tool
description at the service source:
```go
// Create opens a customer support ticket and returns its stable ticket ID.
// @example {"customer":"Pat","subject":"Cannot log in"}
func (s *TicketService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
```
Endpoint comments, request field names, `description` tags, and `@example` blocks
are the model's map of your service. A vague handler comment often looks like a
reasoning failure from the outside.
## 3. Inspect agent run history
After a chat turn, list recent runs for that agent:
```sh
micro inspect agent support
```
The output shows the run id, status, number of recorded events, the last event,
errors, and a short trace id when tracing is configured. Narrow the list while you
iterate:
```sh
micro inspect agent support --limit 5
micro inspect agent support --status timeout
micro inspect agent support --trace abc123
micro inspect agent support --json
```
Useful statuses include `done`, `refused`, `timeout`, `rate_limited`, `canceled`,
and `error`. Use `--json` when you want exact timestamps, trace/span ids, and error
kinds for a bug report.
Run timelines are stored in the agent's state store under that agent's scoped
state (`agent/<name>/runs/...`). The persisted timeline is recorded even without
an OpenTelemetry exporter, so `micro inspect agent` remains useful in local
no-secret development.
## 4. See tool calls as they happen
When you are embedding an agent in Go and need live tool visibility, use the
streaming API instead of waiting for the final answer:
```go
stream, err := agent.StreamAsk(ctx, ag, "Create a ticket for Pat")
if err != nil {
return err
}
for {
ev, err := stream.Recv()
if err != nil {
break
}
switch ev.Type {
case agent.StreamEventToolStart:
log.Printf("tool start: %s %#v", ev.ToolCall.Name, ev.ToolCall.Input)
case agent.StreamEventToolEnd:
log.Printf("tool end: %s %#v", ev.ToolCall.Name, ev.Result)
case agent.StreamEventToken:
fmt.Print(ev.Token)
}
}
```
For custom audit logging, wrap the tool execution boundary. Wrappers observe every
call and result, including guardrail refusals:
```go
wrapped := micro.AgentWrapTool(func(next ai.ToolHandler) ai.ToolHandler {
return func(ctx context.Context, call ai.ToolCall) ai.ToolResult {
if run, ok := ai.RunInfoFrom(ctx); ok {
log.Printf("run=%s agent=%s tool=%s", run.RunID, run.Agent, call.Name)
}
res := next(ctx, call)
if res.Refused != "" {
log.Printf("tool refused: %s reason=%s", call.Name, res.Refused)
}
return res
}
})
ag := micro.NewAgent("support", wrapped)
```
Use this when you need request/response payloads in your own logs. By default,
Go Micro records safe run metadata; raw prompt input is not persisted unless the
agent is configured with `agent.TraceInputs(true)`.
## 5. Inspect memory and plans
Default agent memory is store-backed and scoped to the agent name. A restarted
agent with the same `micro.WithStore(...)` and name reloads conversation history
from the `history` key in `agent/<name>` state. If you pass `micro.WithMemory(...)`,
you own that backend; if you pass `agent.NewInMemory(...)`, memory disappears on
restart.
The built-in `plan` tool also saves the current plan to the same scoped agent
state, so a later turn can pick up the saved plan. When memory does not persist,
check that all of these are stable across restarts:
- the agent name (`micro.NewAgent("support", ...)`),
- the configured store backend (`micro.WithStore(...)` or the process default),
- whether a custom in-memory `Memory` implementation replaced the default,
- whether compaction/retrieval limits are intentionally hiding older turns from
the active model context.
## 6. Inspect workflow handoffs
If a flow triggered the agent, inspect the flow too. The flow history tells you
which durable stage dispatched to the agent and whether a run is still pending:
```sh
micro inspect flow intake
micro inspect flow intake --pending
micro inspect flow intake --stage notify
micro inspect flow intake --json
```
The older flow-specific command remains available for listing runs:
```sh
micro flow runs intake
```
Use the flow run id and the agent run id together when debugging handoffs: the
flow explains why work started and where it checkpointed; the agent run explains
which model/tool steps happened after the handoff.
## 7. Add traces when metadata is not enough
For local CLI debugging, `micro inspect` is the fastest path. For production or
multi-service debugging, configure an OpenTelemetry tracer provider on the agent:
```go
ag := micro.NewAgent("support",
micro.AgentTraceProvider(tp),
)
```
Trace ids flow into the recorded run summaries, so you can pivot between
`micro inspect agent support --trace <prefix>` and your trace backend. Keep
`agent.TraceInputs(true)` off unless your observability backend is approved to
store prompt content.
## Troubleshooting table
| Symptom | What to inspect | Common fix |
| --- | --- | --- |
| Agent answers without calling a service | `micro services`, direct `micro call`, then `micro inspect agent <name>` | Register the service, include it in `micro.AgentServices(...)`, or improve endpoint comments and examples. |
| Agent loops or burns steps | `micro inspect agent <name> --status error` and wrapper logs | Add or lower `micro.AgentMaxSteps(...)` / `micro.AgentLoopLimit(...)`; move predictable work into a flow. |
| Tool is refused before it runs | Wrapper logs, `ToolResult.Refused`, `micro inspect agent <name> --status refused` | Update `micro.AgentApproveTool(...)` policy or prompt the user for explicit approval before retrying. |
| Memory is missing after restart | Agent name, store backend, `WithMemory`, compaction/retrieval settings | Use the default store-backed memory with a persistent store, or persist your custom memory backend. |
| Flow handoff appears stuck | `micro inspect flow <flow> --pending`, then `micro inspect agent <agent>` | Resume or fail the pending flow run; confirm the dispatched agent completed or timed out. |
| Provider failed or timed out | `micro inspect agent <name> --status timeout` / `--status rate_limited` | Retry with the same provider/model, raise deadlines where appropriate, or enable provider retries for transient errors. |
| Tool call appears as assistant text | Agent run history and provider conformance checks | Keep provider packages current; Go Micro normalizes provider-emitted text tool calls, and conformance tests guard this behavior. |
## What to include in a bug report
When you cannot explain the run locally, include:
```sh
micro inspect agent <agent> --limit 5 --json
micro inspect flow <flow> --limit 5 --json
micro services
micro call <service> <Handler.Method> '{}'
```
Redact secrets and user data. If you enabled `agent.TraceInputs(true)`, inspect the
JSON before sharing it because prompts may be present.
@@ -84,7 +84,7 @@ message Response {
```bash
# Install protoc-gen-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@v6
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# Generate Go code
protoc --proto_path=. \
@@ -93,8 +93,6 @@ protoc --proto_path=. \
proto/helloworld.proto
```
> **Note:** Use a specific version instead of `@latest` to avoid module path conflicts. See [releases](https://github.com/micro/go-micro/releases) for the latest version.
### Server Implementation
```go
@@ -90,7 +90,7 @@ Update your proto generation:
```bash
# Install protoc-gen-micro
go install go-micro.dev/v6/cmd/protoc-gen-micro@v6
go install go-micro.dev/v6/cmd/protoc-gen-micro@latest
# Generate both gRPC and Go Micro code
protoc --proto_path=. \
@@ -100,7 +100,6 @@ protoc --proto_path=. \
proto/hello.proto
```
> **Note:** Use a specific version instead of `@latest` to avoid module path conflicts. See [releases](https://github.com/micro/go-micro/releases) for the latest version.
This generates:
- `hello.pb.go` - Protocol Buffers types
@@ -32,7 +32,7 @@ go mod tidy
Update the CLI too:
```bash
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
## 2. TLS is verified by default
@@ -0,0 +1,227 @@
---
layout: default
---
# Your First Agent
This walkthrough builds the smallest useful Go Micro agent path: one service
with typed endpoints, one agent scoped to that service, and one CLI conversation
that proves the agent can use the service as a tool. It is the 0→1 version of
the services → agents → workflows lifecycle: build capability first, add
intelligence on top, then keep a clear path toward flows when the work needs to
run on events or schedules.
## Runnable reference first
If you want to run the lifecycle before copying code, start with the maintained support-desk example from the repository root:
```sh
go run ./examples/support
```
It uses a deterministic mock model by default, so it needs no provider key, and it exercises the same shape this guide teaches: services become tools, an agent uses them, and a flow can trigger the work. Use this guide when you are ready to build the smaller 0→1 version yourself.
## What you'll build
A tiny task assistant:
1. A `task` service exposes `Create` and `List` endpoints.
2. An `assistant` agent is scoped to the `task` service.
3. `micro run` starts both in the local harness.
4. `micro chat` asks the agent to create and list tasks.
The same service endpoints are normal RPC methods, dashboard/API actions, MCP
tools, and agent tools. You do not write a second integration layer for the
agent.
## Prerequisites
- Go 1.24 or newer.
- The `micro` CLI installed.
- An LLM provider key for live agent calls. For example:
```sh
export ANTHROPIC_API_KEY=sk-ant-...
```
Plain service calls work without a model key; the key is only needed when the
agent reasons over tools.
Run the read-only first-agent preflight before starting the walkthrough:
```sh
micro agent preflight
```
It checks Go, the `micro` binary, provider-key setup, and the default local gateway port without contacting a provider.
## 1. Create a workspace
```sh
mkdir first-agent
cd first-agent
go mod init example.com/first-agent
go get go-micro.dev/v6@v6
```
Add `main.go`:
```go
package main
import (
"context"
"fmt"
"os"
"sync"
micro "go-micro.dev/v6"
)
type CreateRequest struct {
Title string `json:"title"`
}
type CreateResponse struct {
ID string `json:"id"`
Title string `json:"title"`
}
type ListRequest struct{}
type ListResponse struct {
Tasks []CreateResponse `json:"tasks"`
}
type TaskService struct {
mu sync.Mutex
next int
tasks []CreateResponse
}
// Create adds a task to the list.
// @example {"title":"Write first agent guide"}
func (t *TaskService) Create(ctx context.Context, req *CreateRequest, rsp *CreateResponse) error {
t.mu.Lock()
defer t.mu.Unlock()
t.next++
*rsp = CreateResponse{ID: fmt.Sprintf("task-%d", t.next), Title: req.Title}
t.tasks = append(t.tasks, *rsp)
return nil
}
// List returns all known tasks.
// @example {}
func (t *TaskService) List(ctx context.Context, req *ListRequest, rsp *ListResponse) error {
t.mu.Lock()
defer t.mu.Unlock()
rsp.Tasks = append([]CreateResponse(nil), t.tasks...)
return nil
}
func main() {
service := micro.NewService("task")
service.Handle(new(TaskService))
agent := micro.NewAgent("assistant",
micro.AgentServices("task"),
micro.AgentPrompt("You help manage tasks. Use the task service before answering."),
micro.AgentProvider("anthropic"),
micro.AgentAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
)
go agent.Run()
service.Run()
}
```
> Why the comments matter: endpoint comments and `@example` tags become tool
> descriptions, so the agent has enough context to choose `task.Create` and
> `task.List` correctly.
## 2. Run the service and agent
From the same directory:
```sh
micro run
```
The local harness starts the service, gateway, dashboard, MCP tool surface, and
agent playground. You can also verify the service directly before involving the
agent:
```sh
micro call task TaskService.Create '{"title":"Ship the walkthrough"}'
micro call task TaskService.List '{}'
```
## 3. Chat with the agent
In another terminal, ask the agent to use the service:
```sh
micro chat assistant
```
Try:
```text
Create a task called "Review the first-agent walkthrough", then show me all tasks.
```
A healthy run shows the agent calling the task service and then summarizing the
result. If the model refuses to call tools, tighten the prompt so it explicitly
uses the `task` service before answering.
## 4. Know what just happened
- The service registered typed RPC endpoints.
- Go Micro derived tool descriptions from the endpoint names, comments, request
fields, and examples.
- The agent registered as another service with an `Agent.Chat` endpoint.
- `micro chat` sent your message to the agent.
- The agent selected the scoped `task` tools, called them over the same runtime,
and stored conversation history in memory.
That is the core lifecycle: services provide capability, agents use the
capability, and the same runtime can later put the interaction behind a flow.
## 5. Make it a workflow when the path is event-driven
Once the prompt should run because something happened rather than because a
human typed a message, move the handoff into a flow:
```go
flow := micro.NewFlow("task-triage",
micro.FlowTrigger("tasks.created"),
micro.FlowPrompt("Review this new task and decide the next action: {{.Data}}"),
micro.FlowProvider("anthropic"),
micro.FlowAPIKey(os.Getenv("ANTHROPIC_API_KEY")),
)
```
Use flows for deterministic triggers and long-running orchestration; keep the
agent for judgment, tool use, and handoffs when the path is not known up front.
## Troubleshooting
| Symptom | Check |
| --- | --- |
| The agent says it cannot access tasks. | Confirm the agent was created with `micro.AgentServices("task")` and that `micro agent list` shows `assistant`. |
| Tool calls use the wrong fields. | Add or improve doc comments and `@example` tags on the service methods. |
| Plain service calls work but chat fails. | Check that your provider key is exported in the shell that runs `micro run`. |
| You need a no-secret reference path. | Run `make harness` from the Go Micro repository; it exercises the services → agents → workflows lifecycle with a mock provider. |
## Next steps
- Read the [0→hero reference path](zero-to-hero.html) for the CI-verified
lifecycle contract.
- Run [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support) for the no-secret 0→hero support-desk lifecycle.
- Run [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate)
to see planning and delegation across agents.
- Read [Debugging your agent](debugging-agents.html) when a chat turn does not call the tool you expected, loops, refuses a call, loses memory, or fails after a flow handoff.
- Read [Agents and Workflows](agents-and-workflows.html) when you are ready to
compose agents behind durable flows.
@@ -24,6 +24,16 @@ cloud credentials?"
| Deploy | `micro deploy --dry-run` resolves deploy targets without touching remote infrastructure. | `go test ./cmd/micro/cli/deploy -run TestDeployDryRun -count=1` |
| Runtime | Real services, agents, durable flows, store-backed history, delegation, and A2A run with only the model mocked. | `./internal/harness/zero-to-hero-ci/run.sh` and `make provider-conformance-mock` |
## Run the runnable example
From the repository root, start with the support-desk example when you want to see the full lifecycle in one terminal:
```sh
go run ./examples/support
```
It starts typed services, a support agent, an event-driven intake flow, and an approval gate with a deterministic mock model. Change one service method, agent prompt, or guardrail decision and run it again to learn the system by modifying a working path.
## Run the whole no-secret path
From the repository root:
@@ -61,6 +71,11 @@ make provider-conformance-mock
## Reference scenarios
- [`examples/support`](https://github.com/micro/go-micro/tree/master/examples/support)
is the runnable support-desk story: customers, tickets, notify, a support
agent, an intake flow, and an approval gate in one no-secret example.
- [`examples/agent-plan-delegate`](https://github.com/micro/go-micro/tree/master/examples/agent-plan-delegate)
is the smallest runnable planning/delegation example for multiple agents.
- [`internal/harness/plan-delegate`](https://github.com/micro/go-micro/tree/master/internal/harness/plan-delegate)
is the compact 0→hero scenario: real task and notify services, a conductor
agent, a comms agent, plan persistence, delegation, and a workflow handoff.
+7 -3
View File
@@ -16,13 +16,15 @@ It's built on a pluggable architecture of Go interfaces: service discovery, clie
## Learn More
To get started follow the getting started guide.
Otherwise continue to read the docs for more information
about the framework.
Start with [Getting Started](getting-started.html) for install and the first local service. If you want the full services → agents → workflows on-ramp in one walkable sequence, use the [0→hero reference path](guides/zero-to-hero.html): it links the exact scaffold, run, chat, inspect, and deploy dry-run commands covered by CI.
Otherwise continue to read the docs for more information about the framework.
## Contents
- [Getting Started](getting-started.html)
- [0→hero Reference](guides/zero-to-hero.html) - Walk scaffold → run → chat → inspect → deploy dry-run with CI-backed commands
- [Your First Agent](guides/your-first-agent.html) - Build a service-backed agent end to end
- [MCP & AI Agents](mcp.html) - Turn services into AI-callable tools with the Model Context Protocol
- [CLI & Gateway Guide](guides/cli-gateway.html) - Development vs Production modes
- [Quick Start](quickstart.html)
@@ -44,6 +46,8 @@ about the framework.
## AI & Agents
- [0→hero Reference](guides/zero-to-hero.html) - Walk scaffold → run → chat → inspect → deploy dry-run with CI-backed commands
- [Your First Agent](guides/your-first-agent.html) - Build a service-backed agent end to end
- [Building AI-Native Services](guides/ai-native-services.html) - End-to-end tutorial for MCP-enabled services
- [MCP Security Guide](guides/mcp-security.html) - Auth, scopes, rate limiting, and audit logging
- [Tool Description Best Practices](guides/tool-descriptions.html) - Writing docs that make agents effective
+1 -3
View File
@@ -13,11 +13,9 @@ curl -fsSL https://go-micro.dev/install.sh | sh
Or, if you have Go and prefer to build from source:
```bash
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
> **Note:** Use `@v6` (not `@latest`) — it resolves to the newest `v6.x.x` release. Plain `@latest` can currently resolve to a stale pre-rename tag through the public module proxy and fail with a "version constraints conflict"; `@v6` avoids that. To pin an exact version use e.g. `@v6.2.0` (see [releases](https://github.com/micro/go-micro/releases)).
## Create Your First Service
```bash
+1 -3
View File
@@ -28,11 +28,9 @@ For local development, use [`micro run`](guides/micro-run.html) instead.
Install the CLI which includes the server command:
```bash
go install go-micro.dev/v6/cmd/micro@v6
go install go-micro.dev/v6/cmd/micro@latest
```
> **Note:** Use a specific version instead of `@latest` to avoid module path conflicts. See [releases](https://github.com/micro/go-micro/releases) for the latest version.
## Run
Start the server: