ADK (Go)
The Go SDK has no framework adapters — deliberately. Framework glue in an SDK forces that framework into the build of every consumer who does not use it, and it rots on the framework’s release schedule rather than ours. The glue belongs in your agent.
It is about thirty lines.
Do not expose the verdict as a tool
Section titled “Do not expose the verdict as a tool”Same rule as LangChain: a model that can skip the safety check will skip it under instruction pressure — including pressure injected through text it is reading. Put the verdict in your loop, not in the toolset.
Retrieval as a tool is fine. The verdict is not.
The client
Section titled “The client”cfg := vectadyne.Config{App: "release-train"}.FromEnv().WithDefaults()client, err := vectadyne.New(cfg)if err != nil { return err}Memory search as a tool
Section titled “Memory search as a tool”func memoryTool(c *vectadyne.Client) *adk.Tool { return adk.NewTool("recall_governed_memory", "Search governed engineering memory for what is known about this area.", func(ctx context.Context, args struct { Query string `json:"query"` }) (any, error) { hits, err := c.RecallText(ctx, args.Query) if err != nil { return nil, err } return hits, nil })}Note the error is returned rather than swallowed. An agent that silently gets no memory looks the same as one whose corpus is empty, and you will debug the wrong thing.
The governed step, in your loop
Section titled “The governed step, in your loop”assembled, err := client.Assemble(ctx, wire.AssembleRequest{ Action: wire.Action{ ActionType: actionType, Summary: summary, ChangedFiles: files, ScopeKey: scope, }, Purpose: purpose,})if err != nil { return fmt.Errorf("governed assemble: %w", err) // NOT a verdict — see Handle errors}
g := vectadyne.InterpretContext(assembled)if g.Hold { return escalate(g.Reason, g.Blockers)}for _, a := range g.Advisories { instructions = append(instructions, "Governed memory notes: "+a.Message)}Go’s Hold is true for stop — the inverse of Python’s proceed.
Trap 1: the crew shares one client
Section titled “Trap 1: the crew shares one client”An orchestrator holding one client means every member authenticates as the orchestrator. Per-member scoping evaporates and the audit trail records one actor doing everything.
Give each member its own client, or narrow per call with an actor block. And carry identity in
session state so it survives a handoff — this is the one that gets lost silently, because
everything still works.
Trap 2: the whole transcript travels
Section titled “Trap 2: the whole transcript travels”ADK will pass prior conversation content to the next agent unless you say otherwise. That hands member B everything member A retrieved — including memory B’s own credential would not have been allowed to see, which quietly defeats the clearance model.
Restrict what travels to what the next member actually needs. Assert it in a test: run a two-hop handoff and check the second member did not receive the first’s retrieved memory.
Telemetry
Section titled “Telemetry”The SDK has no OpenTelemetry dependency, by design — it would land in the build of every consumer
who does not use OTel. Propagation is a seam: supply an http.Client whose transport injects
traceparent, which is exactly what otelhttp.NewTransport does.
cfg.HTTPClient = &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}If you emit spans, a governance refusal is a span EVENT, never a span ERROR. Span status Error
means the call failed, not that the answer was restrictive. Marking warn as an error teaches every
dashboard that governance is a failure mode — the exact inversion of advisory-first, arriving through
telemetry instead of through the API, and it makes the platform look broken precisely when it is
working.