Adds an opt-in `security` role to `micro loop` and wires it into go-micro's own
loop. On a schedule it dispatches the agent to audit the codebase for real,
exploitable vulnerabilities and file them.
Security gets a deliberately more conservative policy than the other roles,
encoded in .github/loop/prompts/security.md:
- NEVER auto-merges a security change (fixes stay human-reviewed).
- NEVER publishes exploit detail / PoC in a public issue — novel exploitable
findings get a concise `security` + `needs-human` issue (class, location,
impact) routed to private disclosure; only known/public dep CVEs get a
bump PR (no auto-merge).
- Weekly by default (`--security-cron`, 0 6 * * 1); tunable.
The go-micro prompt targets its real attack surface: MCP/A2A gateways, x402
payments, JWT/wrapper auth, provider BaseURL SSRF + key leakage, the agent
tool loop (prompt injection / guardrail bypass), TLS defaults, the loop's own
PAT, and dependency CVEs via govulncheck.
Note: an agent review is not a gate. The deterministic companion — govulncheck
as a required CI check — is a recommended follow-up so known-vulnerable deps
can't merge at all.
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
Backstop for the gate: previously loop-triage only fired on Harness (E2E)
failures, so a red lint or test on master (e.g. the misspell that slipped past
because golangci-lint isn't a required check) produced no fix issue. Now triage
watches all the gate workflows.
- micro loop: `--ci-workflow` accepts a comma-separated list of workflow names,
rendered into the triage workflow_run trigger as a YAML array; the issue names
the actual failed workflow via github.event.workflow_run.name. (generic CLI)
- go-micro: regenerate loop-triage.yml to watch "Harness (E2E)", "Lint",
"Run Tests"; generalize the triage prompt beyond the harness (a lint/test
failure on master is a real regression to fix, not a flake to ignore).
- Docs: update CONTINUOUS_IMPROVEMENT.md triage description.
Note: this is defense-in-depth. The primary fix is making golangci-lint a
required status check so red lint can't merge in the first place — that stays
with the human (branch protection).
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
* feat(loop): go-micro now runs on `micro loop` (dogfood its own tool)
Replace go-micro's five hand-written loop workflows with ones generated by
`micro loop init --roles all`, making "go-micro builds itself with micro loop"
literally true rather than aspirational.
- Generate loop-planner/builder/triage/coherence/release.yml via the CLI with
go-micro's cadence and wiring (planner :59, builder :29, coherence 07:00,
release 23:00; CI gate "Harness (E2E)"; token CODEX_TRIGGER_TOKEN; base master;
tag prefix v). The old loop-architect.yml and loop-devrel.yml become
loop-planner.yml and loop-coherence.yml.
- Move the queue to .github/loop/PRIORITIES.md and add .github/loop/NORTH_STAR.md
(a concise steer pointing to internal/docs/THESIS.md), adopting the loop's
convention.
- Preserve go-micro's rich instructions as editable policy in
.github/loop/prompts/{planner,builder,triage,coherence}.md — the architect
founder-lens + adoption steer, the increment builder, harness-failure triage,
and the DevRel changelog/blog pass — faithfully ported from the old inline
prompts. Behavior is preserved; only the mechanism is now generated.
- CLI refinement the migration surfaced: prompts (and NORTH_STAR/PRIORITIES) are
now write-once — `micro loop init --force` refreshes workflow MECHANICS but
never clobbers customized POLICY. Added renderKeep + a test.
- Update internal/docs/CONTINUOUS_IMPROVEMENT.md (renamed workflows, moved queue,
the prompt-file model, and a note that these files are generated by micro loop).
Verified: build, go test ./cmd/micro/loop/..., golangci-lint (0 issues), gofmt;
`micro loop verify` passes; all generated workflows are valid YAML; re-running
init --force is idempotent and preserves policy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* loop: strip prompt editorial comments before posting to the agent
Verification of the migration surfaced that a dispatch workflow posted the
prompt file's leading <!-- editorial --> header to the agent, and __ISSUE__
inside it got substituted too (e.g. "Keep 4242 literal"). Harmless (invisible
in rendered markdown) but unclean and mildly confusing. The dispatch and triage
body construction now strips <!-- --> blocks with `sed '/<!--/,/-->/d'` before
substituting runtime tokens. Regenerated go-micro's workflows; added a test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
---------
Co-authored-by: Claude <noreply@anthropic.com>
Rework `micro loop` so the workflows are the mechanism and each dispatch role's
instruction is an editable .github/loop/prompts/<role>.md file (the policy).
That split lets any repo — including go-micro itself — customize behavior by
editing prompt files instead of forking the CLI, which is the prerequisite for
go-micro consuming its own tool without losing its richer prompts.
- Add two opt-in roles: `coherence` (README/docs/CHANGELOG alignment) and
`release` (cut the next patch tag on new commits; bakes in the
persist-credentials:false fix so the PAT push isn't clobbered by the
checkout token — the 403 we hit on the live release action).
- `--roles` selects which roles to scaffold (default planner,builder,triage;
`all` for everything); `--tag-prefix`, `--release-cron`, `--coherence-cron`
added. Templates keep the << >> delimiters so GHA ${{ }} passes through;
prompts leave __ISSUE__/__RUNURL__ as runtime tokens the workflow substitutes.
- `micro loop verify` now checks each present role workflow has its prompt.
- README updated with the five roles and a copy-pasteable flag example
(folds in the readability fix from the now-closed #3651).
Verified: build, `go test ./cmd/micro/loop/...`, vet, golangci-lint (0 issues),
gofmt; and an end-to-end `micro loop init --roles all` whose generated
workflows all parse as valid YAML and pass `micro loop verify`.
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
`micro loop init` writes an autonomous improvement loop into any repository —
the same planner/builder/triage loop that maintains go-micro, generalized:
- planner (loop-planner.yml): keeps a ranked queue in .github/loop/PRIORITIES.md
- builder (loop-builder.yml): builds the top open item as a single-concern PR,
auto-merged on green CI
- triage (loop-triage.yml): turns CI failures into scoped fix issues
plus .github/loop/NORTH_STAR.md (direction) and PRIORITIES.md (queue).
The agent adapter is mention-based, not hardcoded to Codex: `--agent @codex`
(or any @mention agent that responds on an issue and can run gh), `--token-secret`,
`--branch`, `--ci-workflow`, and cron flags are the whole config-vs-core boundary.
Templates use << >> delimiters so GitHub Actions' own ${{ }} expressions pass
through untouched. `micro loop verify` checks the wiring and flags the two things
the CLI can't: the token secret and branch protection (the green-CI gate).
Built inside go-micro with the config/core split already drawn, so the workflows
can later be extracted to a standalone reusable-workflows repo without a rewrite.
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
The gateway already serves /mcp/tools on :8080 unconditionally (every
endpoint is an AI-callable tool), but the startup banner only printed an MCP
line when --mcp-address was set — so the live `micro run` experience hid the
harness's signature feature even though it was running, and didn't match the
README. Always advertise MCP Tools on the gateway address; keep the optional
standalone MCP-protocol server (--mcp-address) as a clearly separate line.
No behavior change — banner output only.
Co-authored-by: Claude <noreply@anthropic.com>
Follow-up to #3006:
- provider-conformance: build each harness to a temp binary and run that
instead of 'go run'. 'go run' launches the harness as a child it doesn't
kill on context cancellation, so a timed-out harness (which starts local
services) could be orphaned and outlive the run. Running the built binary
makes the per-run timeout actually terminate the work.
- contract test: skip under -short, and use 'go build ./...' instead of
'go test ./...' (the contract is that the generated service builds). This
keeps the default unit-test suite from shelling out to the toolchain and
the network on every run.
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
Fixes#2988. Brings 'golangci-lint run ./...' to zero issues (was ~373):
- errcheck: explicitly ignore fire-and-forget calls with '_ =' (and a small
errcheck.exclude-functions list for response writes — json Encoder.Encode,
http ResponseWriter.Write, fmt.Fprint*); genuine cases handled.
- unused: remove dead code (unexported decls and dead test helpers) and the
imports they orphaned.
- staticcheck: ST1005 error strings, ST1016 receiver names, S1000/S1017/S1019/
S1023 simplifications, SA4004/SA4006/SA4010 dead code, SA1021 net.IP.Equal,
SA6002 (store *[]byte in sync.Pool).
- govet: fix a context leak (lostcancel) in internal/util/mdns and move
t.Fatal/Fatalf out of goroutines (testinggoroutine) in tests.
- ineffassign, unconvert: mechanical fixes.
CI: the Lint workflow now runs a blocking full-tree 'golangci-lint run' on
pushes and PRs (dropped only-new-issues now that the tree is clean).
Verified: go build, go vet, test compilation, and unit tests for the
behaviourally-touched packages all pass.
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
Co-authored-by: Claude <noreply@anthropic.com>
* docs: compare Go Micro with Google ADK in the comparison guide
Adds a 'vs Agent Frameworks (Google ADK)' section: ADK builds an agent,
Go Micro builds the distributed system the agent lives in (agents are
services in the mesh). Covers the category difference, a feature table,
when to choose each, and MCP/A2A interoperability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* docs: replace ADK comparison slogan with concrete explanation
State plainly what each tool provides (ADK builds an agent process; Go Micro
builds the surrounding service mesh) instead of marketing phrasing.
* lint: apply golangci-lint autofixes; exclude ST1003 and demo errcheck
Mechanical, behaviour-preserving fixes applied by 'golangci-lint run --fix':
gofmt, misspell (US spelling), usestdlibvars (http.Method*/Status*), unconvert,
and the auto-fixable staticcheck simplifications (QF*, S1017/S1019/S1023/S1039).
Config: exclude ST1003 (remaining offenders are exported API renames, e.g.
web.Id, which would break compatibility) and skip errcheck for examples/ and
internal/harness/ (demo code where fire-and-forget is intentional).
Build and test compilation verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* lint: WIP cleanup checkpoint (errcheck config + partial fixes)
Checkpoint of an in-progress golangci-lint cleanup (background pass). Builds
cleanly; lint is not yet zero. Follow-up commit will complete the cleanup and
switch CI to a blocking full-tree lint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
---------
Co-authored-by: Claude <noreply@anthropic.com>
* examples: support desk agent + blog walkthrough
A real-world, runnable example (examples/support): customers/tickets/notify
services become the agent's tools, a flow turns a ticket.created event into
the agent's work, and an approval gate guards the one action that touches a
customer. Runs with no API key (mock model) or against a live provider.
Adds blog/28 'Building a Support Agent in Go' and indexes both.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* fix(new): protoless services by default; fix @latest install (#2985)
micro new now scaffolds a reflection-based service by default — plain Go
types registered via service.Handle, no .proto, no Makefile proto target.
The generated project builds and runs with 'go run .' and zero external
tooling. Protocol Buffers move behind --proto (the crud/pubsub/api
templates imply it). When the proto workflow is used and protoc /
protoc-gen-go / protoc-gen-micro are missing, print exact install
instructions instead of failing with a cryptic plugin error.
Also fixes the 'go install go-micro.dev/v6/cmd/micro@latest' version
constraint conflict: the vanity go-import meta still advertised /v5, so Go
fell back to the bare module and resolved an ancient v1.x tag. Advertise
/v6 (keeping /v5 for existing users) and add a version-pin fallback note to
the install docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* fix(new,install): pin generated go.mod to current go-micro; lead install with prebuilt binary (#2985)
- micro new now requires the exact go-micro version the CLI was built from
(via build info), falling back to 'latest' for dev builds. An explicit
require is also more robust than a bare import: 'go mod tidy' reliably
resolves it, where a requireless go.mod could fail vanity discovery.
- Make the precompiled binary (curl install.sh) the recommended install in
the docs; demote 'go install' to a from-source option with the version-pin
fallback note.
- Sync the stale internal/scripts/install.sh to the working website script
(it expected an old micro-OS-ARCH asset name; releases ship
micro_OS_ARCH.tar.gz).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
* website: add corrected nginx vanity-import config (#2985)
The live go-micro.dev handler echoed the full request path into the
go-import prefix ($host$1), so go install go-micro.dev/v6/cmd/micro@latest
got prefix go-micro.dev/v6/cmd/micro — a package, not the module root — and
Go fell back to the ancient v1.x tags (version constraints conflict).
Add a dedicated /vN location that emits the module root (go-micro.dev/vN)
for any sub-path, and make the catch-all advertise the current module roots
instead of echoing arbitrary paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL
---------
Co-authored-by: Claude <noreply@anthropic.com>
* test(harness): read agent plan from the scoped store
The store-scoping change moved an agent's plan from the default table
key agent/{name}/plan to its own table (database "agent", table {name},
key "plan"). The plan-delegate harness tests still read the old key and
failed with 'not found'; read through store.Scope(mem, "agent", name)
like the agent does.
* docs: orient agents-first across README, landing, and docs overview
Lead with agents (then services and flows), surface MCP + A2A as the
interop story, and frame agents as services. Landing hero and feature
grid reordered agents-first with an A2A gateway card.
* v6: module path go-micro.dev/v6, TLS secure by default, NewService
Cut v6. Three breaking changes, bundled so the major bump is paid once:
- Module path go-micro.dev/v5 -> go-micro.dev/v6 across all imports + go.mod.
- TLS verification on by default (was off). MICRO_TLS_SECURE removed;
MICRO_TLS_INSECURE=true opts out for self-signed/dev.
- micro.NewService(name, opts...) is the canonical service constructor,
symmetric with NewAgent/NewFlow; micro.New kept as a deprecated alias;
the old name-less NewService(opts...) removed. Generators emit NewService.
Also ports the JWT auth token provider in-module (go-micro.dev/v6/auth/jwt/token
on golang-jwt/jwt/v5), dropping the v5-pinned github.com/micro/plugins/v5/auth/jwt
and the deprecated dgrijalva/jwt-go.
Docs/README/landing updated to v6 and @latest; v5->v6 migration guide added;
CHANGELOG cut as [6.0.0]. Blog posts left at their historical versions.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(a2a): Agent2Agent protocol gateway
Add gateway/a2a — exposes registered agents over the open A2A protocol so
agents on other frameworks can discover and call them. Agent Cards are
generated from registry metadata (the same way the MCP gateway derives
tools from service endpoints); incoming A2A tasks translate to the
agent's existing Agent.Chat RPC, so there's no per-agent code.
v1 is the synchronous JSON-RPC binding: message/send returns a completed
Task, tasks/get retrieves it, and Agent Cards are served for discovery;
streaming and push notifications are advertised as unsupported. Run with
'micro a2a serve' (cmd/micro/a2a). Tests cover card generation,
message/send, tasks/get, listing, and unknown-method errors.
* docs: A2A guide, README contents + A2A section, universe A2A check
- Add a Contents table of contents at the top of the README and an A2A
subsection under Building Agents.
- Add the Agent2Agent (A2A) guide and register it in the docs nav.
- Exercise the A2A gateway in the universe harness: the concierge agent
is reached over A2A (message/send -> Agent.Chat -> completed task).
* feat(a2a): outbound client — call external A2A agents
Add a2a.Client (Send/Card) so a Go Micro agent or flow can call an agent
on any framework by URL — the outbound counterpart to the gateway. Wired
in two places: flow.A2A(url) as a workflow step (the cross-framework
Dispatch), and agent delegate to an http(s) URL routes over A2A. The
universe harness now drives the gateway through the client, exercising
both directions. Tests cover client send/card and the round trip.
* docs: A2A both-directions — guide, README, changelog, blog #26
---------
Co-authored-by: Claude <noreply@anthropic.com>
* docs: design note for flow steps + Checkpoint durable execution
* docs: fold in durable-execution decisions (State struct, single Step, run retention, retry)
* docs: rename State.Payload to State.Data
* feat(flow): ordered steps + Checkpoint durable execution
A flow can now be an ordered list of steps (a task with stages) instead
of a single LLM turn. State carries typed Data plus a Stage marker; each
step is checkpointed before and after via a pluggable Checkpoint
(store-backed by default), so a run survives a crash and resumes where it
stopped without re-running completed steps. Flow-level Retry with a
per-step override; runs retained for audit unless DeleteOnSuccess.
Step actions: Call (RPC), LLM (augmented turn), Dispatch (to an agent),
or any StepFunc. Single-step and agent-dispatch flows are unchanged.
* feat(flow): top-level re-exports + durable flow example
Expose the step/checkpoint API from the micro package (FlowSteps,
FlowStep, FlowState, FlowRetry, FlowWithCheckpoint, FlowCall/LLM/Dispatch,
Checkpoint, StoreCheckpoint) and add a runnable, key-free example
demonstrating crash + resume.
* docs: document durable flow steps (guide, README, CLI help)
* docs: blog post + changelog for durable workflows
* fix(flow): scope checkpoint keys by flow name (flow/{name}/runs/{id})
Run keys were flow/runs/{id} — a single global keyspace shared by every
flow on the default store. Namespace them by flow name so each flow's
state is kept apart. StoreCheckpoint now takes a scope argument (the flow
passes its name by default).
* feat(store): Scope handle; scope agent and flow state by name
Add store.Scope(s, database, table) — a store handle that confines every
operation to a database/table without mutating the shared store, so
co-located components don't clobber each other's table (the failure mode
of the global Init(Table(...)) approach).
Use it to keep each agent's memory and plan in its own table
(agent/{name}) and each flow's runs in its own (flow/{name}), instead of
one global table partitioned only by key prefix. Services already scope
by service name.
* feat: consistent state model — service store scoping, flow registry, list/history CLI
- service: scope store via store.Scope (database service / table name),
retiring the Init(store.Table(name)) global-mutation hack; bridge the
default store so handlers using store.DefaultStore stay isolated.
- flow: register in the registry as type=flow while running (with trigger
and step count), deregister on Stop. Live discovery, like agents.
- cli: micro flow list (registry), micro flow runs <name> (durable store),
micro agent history <name> (durable store). list = running, runs/history
= durable, mirroring the service model.
* test: mini-universe end-to-end harness + scheduled GitHub Action
internal/harness/universe boots a small but real go-micro world — four
services, a durable checkout flow that crashes at payment and resumes,
and a guardrailed agent with a tool wrapper reached over RPC — drives the
scenario, asserts the end state (10 checks), and shuts down. Everything
is real except the LLM (mocked), so it's deterministic and needs no key;
-provider anthropic runs it live. Exits non-zero on failure, so it's an
end-to-end test, not just a demo.
Adds .github/workflows/universe.yml (push/PR/daily/dispatch) running the
universe + existing harnesses on the mock provider, plus an opt-in job
that runs live when ANTHROPIC_API_KEY is set. 'make harness' runs them
locally.
* ci: run the live universe job against AtlasCloud (ATLASCLOUD_API_KEY)
* ci: run the live universe job only on schedule or manual dispatch
The deterministic mock job still runs on push/PR/daily; the live
(AtlasCloud) job runs daily and on manual workflow_dispatch only, so
changes don't burn API credits on every PR but can still be checked
against a real model on demand.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat(agent): tool-execution wrappers via WrapTool
Restructure ai.ToolHandler to the structured, ctx-carrying shape that
mirrors a go-micro RPC handler:
func(ctx context.Context, call ai.ToolCall) ai.ToolResult
This reuses the existing ToolCall (with its correlation ID) and
ToolResult types instead of the flat (name, input)->(any, string)
signature, and adds ToolCall.Scan for typed argument access.
Add ai.ToolWrapper and the agent option WrapTool / micro.AgentWrapTool —
the tool-side analogue of client.CallWrapper and server.HandlerWrapper.
Reframe the built-in guardrails (MaxSteps, LoopLimit, ApproveTool) as
composed wrappers around a base handler; developer wrappers compose
outermost, so they observe every call and result, including refusals.
Update all provider call sites, the MCP server and chat handlers, the
integration harnesses, and docs to the new signature.
* examples: add agent-wrap-tool showing AgentWrapTool
A runnable example of tool-execution middleware: an observe wrapper that
times calls and records per-tool metrics (correlated by call ID), and a
retry wrapper that recovers a flaky service call before the model sees
it. Demonstrates outermost-first composition and the wrapper/guardrail
interaction (retries are seen by loop detection).
* docs: note AgentWrapTool in README capabilities and CHANGELOG
* fix(generate): pin scaffolded go.mod to one version constant
The two generators pinned different, stale go-micro versions (v5.24.0
for services, v5.25.0 for agents). Centralize on a single
goMicroVersion constant (v5.29.0) so generated services and agents stay
in sync with the framework and there's one place to bump on release.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Restructure ai.ToolHandler to the structured, ctx-carrying shape that
mirrors a go-micro RPC handler:
func(ctx context.Context, call ai.ToolCall) ai.ToolResult
This reuses the existing ToolCall (with its correlation ID) and
ToolResult types instead of the flat (name, input)->(any, string)
signature, and adds ToolCall.Scan for typed argument access.
Add ai.ToolWrapper and the agent option WrapTool / micro.AgentWrapTool —
the tool-side analogue of client.CallWrapper and server.HandlerWrapper.
Reframe the built-in guardrails (MaxSteps, LoopLimit, ApproveTool) as
composed wrappers around a base handler; developer wrappers compose
outermost, so they observe every call and result, including refusals.
Update all provider call sites, the MCP server and chat handlers, the
integration harnesses, and docs to the new signature.
Co-authored-by: Claude <noreply@anthropic.com>
Follow-up to the merged x402 integration (#2964). Drop the commerce-y
'price' vocabulary for the protocol's own 'amount', and add per-tool
pricing as an operator concern (the way scopes/rate-limits are set at the
gateway).
- x402.Config: Price -> Amount (default), plus Amounts map for per-tool
overrides; AmountFor(tool) resolves per-tool -> default. Add a Require
primitive (per-request enforcement) and LoadConfig for an operator
config file.
- MCP gateway: enforce payment per-tool inside /mcp/call (where scopes
are enforced) using AmountFor, instead of a flat path-based middleware.
- CLI: --x402-price -> --x402-amount; add --x402-config (per-tool file)
to micro mcp serve and micro-mcp-gateway.
- docs: new Payments (x402) guide + nav + README section; blog/22
updated to Amount/Amounts and the config-file model.
Co-authored-by: Claude <noreply@anthropic.com>