Compare commits

..

12 Commits

Author SHA1 Message Date
Codex a2a09635f6 docs: refresh planner priorities for 3982
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-04 22:33:23 +00:00
Asim Aslam 3f17f7b106 Stabilize first-agent broker isolation (#3981)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 23:01:12 +01:00
Asim Aslam 4ed4fca47d docs: align planner queue after 3975 (#3979)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 22:31:56 +01:00
Asim Aslam c2b11a1d31 docs: refresh planner queue for 3974 (#3978)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 22:30:50 +01:00
Asim Aslam f7c2ff0a74 docs: surface first-agent example path (#3975)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 22:22:42 +01:00
Asim Aslam ddf26acc47 docs: refresh planner priorities for 3968 (#3970)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 21:30:07 +01:00
Asim Aslam 54a80bc930 harness: verify first-agent on-ramp (#3967)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 21:05:30 +01:00
Asim Aslam 76f309f253 agent: alias text tool create calls to add (#3964)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 19:59:32 +01:00
Asim Aslam 9a3e6b7e5e docs(priorities): refresh planner queue for 3960 (#3961)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 19:28:25 +01:00
Asim Aslam eb76367b0b Stabilize agent conformance marker retry (#3959)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 19:02:09 +01:00
Asim Aslam 58b8eb3e6b docs(priorities): refresh planner queue for 3954 (#3956)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 18:29:59 +01:00
Asim Aslam f12788bd80 Preserve completed agent plan steps (#3953)
Co-authored-by: Codex <codex@openai.com>
2026-07-04 17:59:30 +01:00
13 changed files with 222 additions and 29 deletions
+4 -5
View File
@@ -21,10 +21,9 @@ changes, architectural rewrites. Those go to the human.
## Work queue (ranked)
1. **Mark AtlasCloud delegated plan steps complete** ([#3946](https://github.com/micro/go-micro/issues/3946)) — the latest live Harness (E2E) run got farther than the earlier task-call blocker: AtlasCloud/minimax-m3 created all three tasks and sent the delegated notification (`tasks=3 notify=1`), but the persisted conductor plan still left the delegation step unfinished. This is the top Now-phase item because it is the active red master conformance gate and directly tests whether services → agents → workflows state stays coherent after real delegation side effects.
2. **Stabilize AtlasCloud guarded delegate conformance** ([#3917](https://github.com/micro/go-micro/issues/3917)) — #3948 just merged stronger AtlasCloud delegate prompting and tagged fallback coverage, so this may be resolved by the next live run, but the issue is still open and has recurred multiple times. Keep it high until master's provider harness proves the guarded delegate reaches the approval-refusal path deterministically, then drop it immediately rather than re-queueing done conformance work.
3. **Execute AtlasCloud plan-delegate task tool calls** ([#3935](https://github.com/micro/go-micro/issues/3935)) — #3942 appears to have moved the live plan-delegate path past the tagged task-call failure, but the issue remains open. Leave it below the newer #3946 completion-state failure and close/drop it once the maintainers confirm the task-call side is fixed by the same live harness evidence.
4. **Broaden provider streaming and keep chat/A2A streaming end to end** ([#3903](https://github.com/micro/go-micro/issues/3903)) — once the live Now-phase conformance gates are green, streaming is the highest developer-visible Next-phase seam. Real chat and long-running A2A tasks need token streaming to stay coherent from provider → `ai.Stream``micro chat` → A2A `message/stream`, with mock/default CI coverage plus key-gated live provider checks and safe fallback for non-streaming providers.
5. **Trace agent runs as OpenTelemetry spans** ([#3908](https://github.com/micro/go-micro/issues/3908)) — the blog/README/roadmap story promises an operable harness, and the developer on-ramp now includes chat, inspect, and run-history checkpoints. The next observability gap is production-grade trace correlation for `RunInfo`: steps, tool calls, delegation, status, durations, and failures should be visible as spans while defaulting to no-op when tracing is not configured.
1. **Require delegated notify before plan-delegate completion** ([#3972](https://github.com/micro/go-micro/issues/3972)) — #3981 closed the first-agent broker isolation gap, leaving this as the latest live AtlasCloud plan/delegate harness regression. A tasks-complete-but-notify-missing run undermines evaluator trust at the agents → workflows seam, so it stays at the top until the harness can prove delegated work only completes after the required notification.
2. **Surface the first-agent and 0→hero example paths in the CLI** ([#3983](https://github.com/micro/go-micro/issues/3983)) — the README, website docs, and examples now describe a strong on-ramp, but adoption still depends on users finding those paths after install. Current goal is developer adoption, so the queue should keep a CI-verifiable CLI wayfinding task near the top instead of drifting entirely into internal conformance and observability work.
3. **Broaden provider streaming and keep chat/A2A streaming end to end** ([#3903](https://github.com/micro/go-micro/issues/3903)) — streaming remains the highest developer-visible Next-phase seam after the current conformance and wayfinding gaps. Real chat and long-running A2A tasks need token streaming to stay coherent from provider → `ai.Stream``micro chat` → A2A `message/stream`, with mock/default CI coverage plus key-gated live provider checks and safe fallback for non-streaming providers.
4. **Trace agent runs as OpenTelemetry spans** ([#3908](https://github.com/micro/go-micro/issues/3908)) — the blog/README/roadmap story promises an operable harness, and the developer on-ramp now includes chat, inspect, and run-history checkpoints. The next observability gap is production-grade trace correlation for `RunInfo`: steps, tool calls, delegation, status, durations, and failures should be visible as spans while defaulting to no-op when tracing is not configured.
_Seeded by Claude Code from the roadmap + open issues; thereafter maintained by the
architecture-review pass._
+3
View File
@@ -440,9 +440,12 @@ resp, _ := m.Generate(ctx, &ai.Request{Prompt: "hello"})
## Examples
New to agents? Follow the [first-agent on-ramp](#first-agent-on-ramp), then use the [examples index](examples/README.md) for the full services → agents → workflows map.
- [hello-world](examples/hello-world/) — Basic RPC service
- [multi-service](examples/multi-service/) — Multiple services in one binary
- [mcp](examples/mcp/) — MCP integration with AI agents
- [first-agent](examples/first-agent/) — Smallest provider-free service-backed agent
- [agent-plan-delegate](examples/agent-plan-delegate/) — Agent planning and multi-agent delegation
- [agent-durable](examples/agent-durable/) — Checkpoint and resume an agent run without replaying completed tool side effects
- [grpc-interop](examples/grpc-interop/) — Call go-micro from any gRPC client
+6 -2
View File
@@ -460,7 +460,7 @@ func (a *agentImpl) Run() error {
a.setup()
}
a.server = server.NewServer(
serverOpts := []server.Option{
server.Name(a.opts.Name),
server.Address(a.opts.Address),
server.Registry(a.opts.Registry),
@@ -468,7 +468,11 @@ func (a *agentImpl) Run() error {
"type": "agent",
"services": strings.Join(a.opts.Services, ","),
}),
)
}
if a.opts.Broker != nil {
serverOpts = append(serverOpts, server.Broker(a.opts.Broker))
}
a.server = server.NewServer(serverOpts...)
_ = pb.RegisterAgentHandler(a.server, a)
+54 -3
View File
@@ -191,13 +191,14 @@ func askWithConformanceRetry(ctx context.Context, a Agent, initialPrompt string,
}
sawRequiredTool := sawTool == nil || *sawTool
sawRequiredDelegate := sawBlockedDelegate == nil || *sawBlockedDelegate
if sawRequiredTool && sawRequiredDelegate {
hasMarker := responseHasConformanceMarker(resp)
if sawRequiredTool && sawRequiredDelegate && hasMarker {
return resp, nil
}
if attempt == maxAttempts {
break
}
prompt = nextConformanceRetryPrompt(sawRequiredTool, sawRequiredDelegate)
prompt = nextConformanceRetryPrompt(sawRequiredTool, sawRequiredDelegate, hasMarker)
}
return resp, nil
}
@@ -231,17 +232,26 @@ func TestAgentProviderConformanceAtlasCloudPromptRequiresTaggedDelegateFallback(
}
}
func nextConformanceRetryPrompt(sawTool, sawBlockedDelegate bool) string {
func nextConformanceRetryPrompt(sawTool, sawBlockedDelegate, hasMarker bool) string {
switch {
case !sawTool:
return "The previous response did not call the required conformance_echo tool. Retry the same conformance check now: you must call conformance_echo exactly once with input {\"value\":\"agent-conformance\"} before any final answer, then include the tool result marker in the final answer."
case !sawBlockedDelegate:
return "The previous response called conformance_echo but did not attempt the required guarded delegation. Continue the same conformance check now: call delegate exactly once with input {\"task\":\"summarize the conformance marker\",\"to\":\"blocked-reviewer\"}; do not answer in prose until that delegate call has been attempted. If native tool_calls are unavailable, emit exactly <tool_call name=\"delegate\">{\"task\":\"summarize the conformance marker\",\"to\":\"blocked-reviewer\"}</tool_call>. The delegate is expected to be refused by policy; include that refusal and the agent-conformance marker in the final answer."
case !hasMarker:
return "The previous response completed the required tool calls but omitted the conformance marker. Continue the same conformance check now: do not call more tools; answer with the prior echo result marker agent-conformance-ok and mention the guarded delegate refusal."
default:
return "Retry the provider conformance check and include the agent-conformance marker in the final answer."
}
}
func responseHasConformanceMarker(resp *Response) bool {
if resp == nil {
return false
}
return strings.Contains(resp.Reply, "agent-conformance-ok") || strings.Contains(resp.Reply, "agent-conformance")
}
func validateConformanceRequest(req *ai.Request, opts ai.Options) error {
if req.Prompt == "" {
return errors.New("missing prompt")
@@ -332,6 +342,47 @@ func TestAgentProviderConformanceRetriesMissingTool(t *testing.T) {
}
}
func TestAgentProviderConformanceRetriesMissingMarker(t *testing.T) {
var attempts int
fakeGen = func(ctx context.Context, opts ai.Options, req *ai.Request) (*ai.Response, error) {
attempts++
if err := validateConformanceRequest(req, opts); err != nil {
return nil, err
}
if attempts == 1 {
return &ai.Response{Reply: "called conformance_echo and handled guarded delegate refusal without the required marker"}, nil
}
return &ai.Response{Reply: "agent-conformance-ok after guarded delegate refusal"}, nil
}
defer func() { fakeGen = nil }()
sawTool := true
sawBlockedDelegate := true
a := New(
Name("conformance-retry-marker"),
Provider("fake"),
WithRegistry(registry.NewMemoryRegistry()),
WithStore(store.NewMemoryStore()),
WithMemory(NewInMemory(4)),
WithTool("conformance_echo", "Echo a conformance value.", map[string]any{
"value": map[string]any{"type": "string"},
}, func(ctx context.Context, input map[string]any) (string, error) {
return `{"marker":"agent-conformance-ok"}`, nil
}),
)
resp, err := askWithConformanceRetry(context.Background(), a, "Run the provider conformance check.", &sawTool, &sawBlockedDelegate)
if err != nil {
t.Fatalf("Ask: %v", err)
}
if attempts != 2 {
t.Fatalf("attempts = %d, want retry after missing marker", attempts)
}
if !strings.Contains(resp.Reply, "agent-conformance-ok") {
t.Fatalf("Reply = %q, want conformance marker", resp.Reply)
}
}
func TestAgentProviderConformanceRetriesMissingDelegate(t *testing.T) {
var attempts int
fakeGen = func(ctx context.Context, opts ai.Options, req *ai.Request) (*ai.Response, error) {
+9
View File
@@ -5,6 +5,7 @@ import (
"time"
"go-micro.dev/v6/ai"
"go-micro.dev/v6/broker"
"go-micro.dev/v6/client"
"go-micro.dev/v6/flow"
"go-micro.dev/v6/registry"
@@ -44,6 +45,7 @@ type Options struct {
Address string
Registry registry.Registry
Client client.Client
Broker broker.Broker
Store store.Store
HistoryLimit int
@@ -192,6 +194,13 @@ func WithClient(c client.Client) Option {
return func(o *Options) { o.Client = c }
}
// WithBroker sets the broker used by the agent service endpoint. Use an
// in-memory broker in local harnesses/tests to avoid sharing the package-wide
// default broker listener across concurrently running examples.
func WithBroker(b broker.Broker) Option {
return func(o *Options) { o.Broker = b }
}
// WithStore sets the store for agent memory.
func WithStore(s store.Store) Option {
return func(o *Options) { o.Store = s }
+35 -14
View File
@@ -48,13 +48,7 @@ func (a *agentImpl) executeTextToolCalls(ctx context.Context, reply string, tool
}
func parseTextToolCalls(text string, tools []ai.Tool) []ai.ToolCall {
allowed := map[string]bool{}
for _, tool := range tools {
allowed[tool.Name] = true
if tool.OriginalName != "" {
allowed[tool.OriginalName] = true
}
}
allowed := textToolNames(tools)
if len(allowed) == 0 {
return nil
}
@@ -70,6 +64,33 @@ func parseTextToolCalls(text string, tools []ai.Tool) []ai.ToolCall {
return nil
}
func textToolNames(tools []ai.Tool) map[string]string {
allowed := map[string]string{}
for _, tool := range tools {
addTextToolName(allowed, tool.Name, tool.Name)
if tool.OriginalName != "" {
addTextToolName(allowed, tool.OriginalName, tool.Name)
}
}
return allowed
}
func addTextToolName(allowed map[string]string, name, canonical string) {
if name == "" || canonical == "" {
return
}
allowed[name] = canonical
// Some OpenAI-compatible models describe an idempotent Add endpoint as a
// creation action and emit the otherwise-correct service tool with a Create
// suffix in text-only tool-call markup. Keep the fallback bounded by the
// offered service tool prefix so ordinary unknown tools remain ignored.
for _, suffix := range []string{"_Add", ".Add"} {
if strings.HasSuffix(name, suffix) {
allowed[strings.TrimSuffix(name, suffix)+strings.Replace(suffix, "Add", "Create", 1)] = canonical
}
}
}
func jsonCandidates(text string) []string {
trimmed := strings.TrimSpace(text)
var out []string
@@ -92,7 +113,7 @@ func jsonCandidates(text string) []string {
return out
}
func decodeTextToolCalls(candidate string, allowed map[string]bool) []ai.ToolCall {
func decodeTextToolCalls(candidate string, allowed map[string]string) []ai.ToolCall {
var root any
if err := json.Unmarshal([]byte(candidate), &root); err != nil {
return nil
@@ -100,7 +121,7 @@ func decodeTextToolCalls(candidate string, allowed map[string]bool) []ai.ToolCal
return collectTextToolCalls(root, allowed)
}
func collectTextToolCalls(v any, allowed map[string]bool) []ai.ToolCall {
func collectTextToolCalls(v any, allowed map[string]string) []ai.ToolCall {
switch x := v.(type) {
case []any:
var out []ai.ToolCall
@@ -121,20 +142,20 @@ func collectTextToolCalls(v any, allowed map[string]bool) []ai.ToolCall {
if input == nil {
input = call.Arguments
}
if name == "" || !allowed[name] || input == nil {
if name == "" || allowed[name] == "" || input == nil {
return nil
}
id := call.ID
if id == "" {
id = fmt.Sprintf("text-call-%s", strings.ReplaceAll(name, ".", "_"))
}
return []ai.ToolCall{{ID: id, Name: name, Input: input}}
return []ai.ToolCall{{ID: id, Name: allowed[name], Input: input}}
default:
return nil
}
}
func decodeTaggedTextToolCalls(text string, allowed map[string]bool) []ai.ToolCall {
func decodeTaggedTextToolCalls(text string, allowed map[string]string) []ai.ToolCall {
var out []ai.ToolCall
for _, match := range singleTaggedToolCall.FindAllStringSubmatch(text, -1) {
if len(match) < 3 {
@@ -150,7 +171,7 @@ func decodeTaggedTextToolCalls(text string, allowed map[string]bool) []ai.ToolCa
continue
}
name := taggedToolName(tag)
if name == "" || !allowed[name] {
if name == "" || allowed[name] == "" {
continue
}
var input map[string]any
@@ -159,7 +180,7 @@ func decodeTaggedTextToolCalls(text string, allowed map[string]bool) []ai.ToolCa
}
out = append(out, ai.ToolCall{
ID: fmt.Sprintf("text-call-%s", strings.ReplaceAll(name, ".", "_")),
Name: name,
Name: allowed[name],
Input: input,
})
}
+16
View File
@@ -40,3 +40,19 @@ func TestParseTextToolCallsFunctionTaggedMarkup(t *testing.T) {
t.Fatalf("title = %v, want Design", got)
}
}
func TestParseTextToolCallsCreateAliasForAddTool(t *testing.T) {
tools := []ai.Tool{{Name: "task_TaskService_Add", OriginalName: "task.TaskService.Add"}}
reply := `<tool_call>{"name":"task_TaskService_Create","arguments":{"title":"Design"}}</tool_call>`
calls := parseTextToolCalls(reply, tools)
if len(calls) != 1 {
t.Fatalf("parseTextToolCalls returned %d calls, want 1: %+v", len(calls), calls)
}
if calls[0].Name != "task_TaskService_Add" {
t.Fatalf("call name = %q, want canonical task_TaskService_Add", calls[0].Name)
}
if got := calls[0].Input["title"]; got != "Design" {
t.Fatalf("title = %v, want Design", got)
}
}
+12 -2
View File
@@ -19,6 +19,7 @@ import (
"go-micro.dev/v6/agent"
"go-micro.dev/v6/ai"
"go-micro.dev/v6/broker"
"go-micro.dev/v6/client"
"go-micro.dev/v6/registry"
"go-micro.dev/v6/selector"
@@ -92,9 +93,17 @@ func runFirstAgent() error {
ai.Register("first-agent-mock", newMock)
reg := registry.NewMemoryRegistry()
cl := client.NewClient(client.Registry(reg), client.Selector(selector.NewSelector(selector.Registry(reg))))
br := broker.NewMemoryBroker()
if err := br.Init(); err != nil {
return fmt.Errorf("init broker: %w", err)
}
if err := br.Connect(); err != nil {
return fmt.Errorf("connect broker: %w", err)
}
defer br.Disconnect()
cl := client.NewClient(client.Registry(reg), client.Selector(selector.NewSelector(selector.Registry(reg))), client.Broker(br))
notes := service.New(service.Name("notes"), service.Address("127.0.0.1:0"), service.Registry(reg), service.Client(cl), service.HandleSignal(false))
notes := service.New(service.Name("notes"), service.Address("127.0.0.1:0"), service.Registry(reg), service.Client(cl), service.Broker(br), service.HandleSignal(false))
if err := notes.Handle(new(NotesService)); err != nil {
return fmt.Errorf("handle notes: %w", err)
}
@@ -110,6 +119,7 @@ func runFirstAgent() error {
agent.Provider("first-agent-mock"),
agent.WithRegistry(reg),
agent.WithClient(cl),
agent.WithBroker(br),
agent.WithStore(store.NewMemoryStore()),
)
agentErr := make(chan error, 1)
@@ -17,6 +17,7 @@ func TestZeroToHeroReferenceDocs(t *testing.T) {
"go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1",
"go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1",
"go test ./cmd/micro/cli/deploy -run TestDeployDryRun -count=1",
"go test ./examples/first-agent -run TestRunFirstAgent -count=1",
"go test ./examples/support -run 'TestRunSupportMockSmoke|TestZeroToHeroReadmeDocumentsLifecycle' -count=1",
"./internal/harness/zero-to-hero-ci/run.sh",
"go run ./internal/harness/agent-flow",
@@ -93,6 +94,36 @@ func TestFirstAgentWayfindingDocs(t *testing.T) {
"internal/website/docs/guides/zero-to-hero.md",
},
},
{
name: "README examples list",
file: filepath.Join(root, "README.md"),
heading: "## Examples",
links: []string{
"examples/README.md",
"examples/first-agent/",
},
},
{
name: "repository examples index",
file: filepath.Join(root, "examples", "README.md"),
heading: "## Recommended first-agent path",
links: []string{
"./first-agent/",
"./support/",
},
},
{
name: "website examples index",
file: filepath.Join(root, "internal", "website", "docs", "examples", "index.md"),
heading: "## Start here",
links: []string{
"https://github.com/micro/go-micro/tree/master/examples/first-agent",
"../guides/no-secret-first-agent.html",
"../guides/your-first-agent.html",
"../guides/debugging-agents.html",
"../guides/zero-to-hero.html",
},
},
{
name: "website getting-started on-ramp",
file: filepath.Join(root, "internal", "website", "docs", "getting-started.md"),
@@ -129,6 +160,8 @@ func TestNoSecretFirstAgentTranscript(t *testing.T) {
guide := readFile(t, filepath.Join(root, "internal", "website", "docs", "guides", "no-secret-first-agent.md"))
for _, want := range []string{
"go run ./examples/first-agent",
"go test ./examples/first-agent -run TestRunFirstAgent -count=1",
"go run ./examples/support",
"go test ./examples/support -run TestRunSupportMockSmoke -count=1",
"make harness",
@@ -169,6 +202,23 @@ func TestNoSecretFirstAgentTranscript(t *testing.T) {
}
}
func TestFirstAgentWayfindingTargetsExist(t *testing.T) {
root := filepath.Clean(filepath.Join("..", "..", ".."))
for _, target := range []string{
"examples/README.md",
"examples/first-agent/README.md",
"internal/website/docs/examples/index.md",
"internal/website/docs/guides/no-secret-first-agent.md",
"internal/website/docs/guides/your-first-agent.md",
"internal/website/docs/guides/debugging-agents.md",
"internal/website/docs/guides/zero-to-hero.md",
} {
if _, err := os.Stat(filepath.Join(root, filepath.FromSlash(target))); err != nil {
t.Fatalf("first-agent wayfinding target %s disappeared: %v", target, err)
}
}
}
func firstMarkdownSection(t *testing.T, doc, heading string) string {
t.Helper()
start := strings.Index(doc, heading)
+1
View File
@@ -14,5 +14,6 @@ go test ./internal/harness/zero-to-hero-ci -run 'TestNoSecretFirstAgentTranscrip
# runtime and mock only the LLM provider. The support example is the maintained
# runnable 0→hero app; keep it in this CI path so its documented run/chat/inspect
# journey cannot drift from the framework.
go test ./examples/first-agent -run TestRunFirstAgent -count=1
go test ./examples/support -run 'TestRunSupportMockSmoke|TestZeroToHeroReadmeDocumentsLifecycle' -count=1
go test ./internal/harness/universe ./internal/harness/plan-delegate -run 'Test.*Harness|TestPlanDelegateEndToEnd|TestPlanDelegateFlowHandoff' -count=1
+9
View File
@@ -10,9 +10,12 @@ agents → workflows lifecycle.
## Start here
For the provider-free first-agent route, run [`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent), then follow [No-secret First Agent](../guides/no-secret-first-agent.html), [Your First Agent](../guides/your-first-agent.html), [Debugging your agent](../guides/debugging-agents.html), and the [0→hero Reference](../guides/zero-to-hero.html).
| 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. |
| Provider-free first agent | [`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent) | Smallest service-backed agent with a deterministic mock model; no provider key required. |
| 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. |
@@ -25,10 +28,16 @@ agents → workflows lifecycle.
- [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.
- [No-secret First Agent](../guides/no-secret-first-agent.html) → run
[`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent)
first for the smallest provider-free agent transcript.
- [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.
- [Debugging your agent](../guides/debugging-agents.html) → keep
[`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent)
nearby as the smallest mock-model reproduction before inspecting richer runs.
- [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.
@@ -25,11 +25,17 @@ end to end with no secrets.
## Transcript
From a fresh clone of the repository:
From a fresh clone of the repository, first run the smallest service-backed agent:
```sh
git clone https://github.com/micro/go-micro.git
cd go-micro
go run ./examples/first-agent
```
Then run the maintained support-agent transcript that exercises the full lifecycle:
```sh
go run ./examples/support
```
@@ -56,9 +62,10 @@ trigger and inspect the work.
## CI-backed check
Run the same deterministic path as a focused test:
Run the same deterministic paths as focused tests:
```sh
go test ./examples/first-agent -run TestRunFirstAgent -count=1
go test ./examples/support -run TestRunSupportMockSmoke -count=1
```
+14 -1
View File
@@ -24,12 +24,19 @@ cloud credentials?"
| Chat | `micro chat` remains the interactive agent entry point. | `go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1` |
| Inspect | `micro inspect agent`, `micro inspect flow`, and `micro flow runs` remain discoverable for run history. | `go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1` |
| Deploy | `micro deploy --dry-run` resolves deploy targets without touching remote infrastructure. | `go test ./cmd/micro/cli/deploy -run TestDeployDryRun -count=1` |
| Smallest first agent | `examples/first-agent` runs one service-backed agent with a deterministic mock model and no provider key. | `go test ./examples/first-agent -run TestRunFirstAgent -count=1` |
| Runtime reference app | `examples/support` runs typed services, an agent using those services as tools, an event-driven flow handoff, and an approval gate with only the model mocked. | `go test ./examples/support -run 'TestRunSupportMockSmoke|TestZeroToHeroReadmeDocumentsLifecycle' -count=1` |
| Runtime harnesses | 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:
From the repository root, start with the smallest service-backed agent when you want the fastest no-secret success path:
```sh
go run ./examples/first-agent
```
Then run the support-desk example when you want to see the full lifecycle in one terminal:
```sh
go run ./examples/support
@@ -68,6 +75,9 @@ go test ./cmd/micro -run TestFirstAgentWalkthroughCLIBoundaries -count=1
go test ./cmd/micro -run TestZeroToHeroCLIBoundaries -count=1
go test ./cmd/micro/cli/deploy -run TestDeployDryRun -count=1
# Smallest no-secret service-backed first agent.
go test ./examples/first-agent -run TestRunFirstAgent -count=1
# Maintained 0→hero support-desk reference app.
go test ./examples/support -run 'TestRunSupportMockSmoke|TestZeroToHeroReadmeDocumentsLifecycle' -count=1
@@ -83,6 +93,9 @@ make provider-conformance-mock
## Reference scenarios
- [`examples/first-agent`](https://github.com/micro/go-micro/tree/master/examples/first-agent)
is the smallest no-secret service-backed agent: one notes service, one scoped
assistant agent, and a deterministic mock model.
- [`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.