Repository navigation
feat(plugin): make agent a distributable gRPC binary (P1-T1) - #22
Merged
Merged
Conversation
Switch the agent plugin from an in-process engine plugin to a distributable
external gRPC binary, mirroring workflow-plugin-infra.
Changes:
- cmd/workflow-plugin-agent/main.go: sdk.Serve → sdk.ServePluginFull
(forward-compat for future CLI/hooks; nil cli+hooks for now).
- .goreleaser.yml: full cross-platform build (linux/darwin/windows ×
amd64/arm64), CGO_ENABLED=0, ldflag -X Version, archives bundle
plugin.json + plugin.contracts.json + LICENSE sidecars, checksums,
release.github, before-hook rewrites plugin.json version+URLs.
- plugin.json: type builtin→external; reconcile capabilities.stepTypes
to runtime truth (2→4: add step.agent_execute, step.provider_test);
add homepage/repository/downloads for the URL-rewrite hook. version
stays 0.0.0 dev-sentinel (goreleaser injects the release tag).
- plugin_contracts_test.go: drop the now-wrong assertion that forbade
step.agent_execute/step.provider_test from plugin.json stepTypes —
that encoded the drift; capabilities must list ALL runtime steps.
- provider_manifest_test.go (new): asserts *AgentPlugin satisfies
sdk.{PluginProvider,ModuleProvider,StepProvider,TypedModuleProvider,
TypedStepProvider,ContractProvider}; Manifest() returns canonical
name; and plugin.json stepTypes/moduleTypes == runtime (P6 drift gate).
No adapter file is needed: *AgentPlugin already implements every sdk
provider interface (Manifest via plugin.go:123; Step/Module/Typed via
typed_contracts.go). The plan's "adapter adds Manifest() only" was
already satisfied — this just wires the existing provider into a binary.
Verified:
- GOWORK=off go build ./... + go test ./... green (3 new tests pass).
- wfctl plugin verify-capabilities (built from workflow origin/main):
OK workflow-plugin-agent 0.10.0 — runtime GetManifest matches plugin.json
(4 steps + agent.provider) when ldflag is injected.
- goreleaser release --snapshot --clean: 6 archives built, each containing
binary + plugin.json + plugin.contracts.json + LICENSE + checksums.txt.
Task 1 of the locked "agent plugin gRPC binary" plan.
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR transitions workflow-plugin-agent from an in-process/builtin plugin shape to a distributable external gRPC plugin binary, aligning runtime-advertised capabilities with plugin.json and adding release packaging via GoReleaser.
Changes:
- Switch the agent binary entrypoint to
sdk.ServePluginFull(...)to support future CLI/hook extension points. - Update
plugin.jsonfortype: external, add release download metadata, and reconcile advertisedcapabilities.stepTypeswith runtime (now 4 steps). - Add/adjust tests to prevent drift between runtime provider capabilities and
plugin.json, and update GoReleaser config for cross-platform builds + release artifacts.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| provider_manifest_test.go | New drift-gate tests for provider interface satisfaction and runtime/plugin.json capability alignment. |
| plugin.json | Mark plugin as external, add homepage/repo/downloads metadata, and expand capabilities.stepTypes to match runtime. |
| plugin_contracts_test.go | Remove the legacy-step prohibition and instead assert non-empty capability advertisement. |
| cmd/workflow-plugin-agent/main.go | Use sdk.ServePluginFull with nil CLI/hooks and build-version injection. |
| .goreleaser.yml | Cross-platform build + packaging, version/URL rewrite hook, checksums, and GitHub release config. |
| .gitignore | Ignore GoReleaser dist/ output and plugin.json.bak backup. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+136
to
+154
| runtime := map[string]bool{} | ||
| for _, s := range New().StepTypes() { | ||
| runtime[s] = true | ||
| } | ||
| declared := map[string]bool{} | ||
| for _, s := range manifest.Capabilities.StepTypes { | ||
| declared[s] = true | ||
| } | ||
|
|
||
| for s := range runtime { | ||
| if !declared[s] { | ||
| t.Fatalf("plugin.json stepTypes missing runtime step %q (drift: plugin.json must list every StepTypes() entry)", s) | ||
| } | ||
| } | ||
| for s := range declared { | ||
| if !runtime[s] { | ||
| t.Fatalf("plugin.json stepTypes declares %q but runtime StepTypes() does not return it", s) | ||
| } | ||
| } |
Comment on lines
+156
to
+168
| if len(manifest.Capabilities.ModuleTypes) == 0 { | ||
| t.Fatal("plugin.json capabilities.moduleTypes is empty") | ||
| } | ||
| runtimeMods := map[string]bool{} | ||
| for _, m := range New().ModuleTypes() { | ||
| runtimeMods[m] = true | ||
| } | ||
| for _, m := range manifest.Capabilities.ModuleTypes { | ||
| if !runtimeMods[m] { | ||
| t.Fatalf("plugin.json moduleTypes declares %q but runtime ModuleTypes() does not return it", m) | ||
| } | ||
| } | ||
| } |
…tors The strict-contracts coverage gate (wfctl plugin validate --strict-contracts) requires every plugin.json capabilities.stepTypes entry to have a strict contract descriptor. step.agent_execute and step.provider_test were registered as legacy/untyped, so the gate failed with missing_step_contract_descriptor for both. Add strict contract DESCRIPTORS for both steps to ContractRegistry() and plugin.contracts.json. The proto messages already existed (AgentExecuteConfig/Input/Output, ProviderTestConfig/Input/Output) — only the descriptor wiring was missing. These two steps remain on the legacy map execution path: their implementations lazy-lookup services (agent-provider-registry, agent-tool-registry) from modular.Application, which the typed gRPC handler signature (TypedStepHandler[C,I,O]) cannot access. CreateTypedStep therefore still declines them (ErrTypedContractNotHandled) and TypedStepTypes() still lists only the two fully-typed steps (provider_models, model_pull). The new descriptors are honest about the I/O shape; the runtime path is unchanged. This is the 'descriptor coverage != typed execution' distinction: a step can carry a strict contract descriptor (so consumers + the gate know its shape) while still executing via the legacy path when it needs host services the typed contract cannot deliver. Verification: - GOWORK=off go build ./... && go test ./... green - /tmp/wfctl plugin validate --file plugin.json --strict-contracts -> OK (0 errors; was 2) - /tmp/wfctl plugin verify-capabilities (ldflag binary, temp v0.10.0) -> OK (0 mismatches) Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the
agentplugin a distributable external gRPC binary (Task 1 of the locked "agent plugin gRPC binary" plan). Mirrorsworkflow-plugin-infra.What
cmd/workflow-plugin-agent/main.go:sdk.Serve→sdk.ServePluginFull(forward-compat for future CLI/hooks; nilcli+hooksfor now)..goreleaser.yml: full cross-platform build (linux/darwin/windows × amd64/arm64),CGO_ENABLED=0, ldflag-X Version, archives bundleplugin.json+plugin.contracts.json+LICENSEsidecars,checksums.txt,release.github, before-hook rewrites plugin.json version+URLs at release time.plugin.json:type: builtin→external; reconcilecapabilities.stepTypesto runtime truth (2 → 4: addstep.agent_execute,step.provider_test); addhomepage/repository/downloadsfor the URL-rewrite hook.versionstays0.0.0dev-sentinel (goreleaser injects the release tag).plugin_contracts_test.go: drop the now-wrong assertion that forbadestep.agent_execute/step.provider_testfrom plugin.json stepTypes — that encoded the drift; capabilities must list ALL runtime steps.provider_manifest_test.go(new): asserts*AgentPluginsatisfiessdk.{PluginProvider,ModuleProvider,StepProvider,TypedModuleProvider,TypedStepProvider,ContractProvider};Manifest()returns canonical name; and plugin.jsonstepTypes/moduleTypes== runtime (P6 drift gate).Why no adapter file
The plan's verified fact said "adapter adds
Manifest()only." ButManifest()is already on*AgentPlugin(plugin.go:123), andsdk.{StepProvider,ModuleProvider,TypedStepProvider,TypedModuleProvider}are already implemented intyped_contracts.go. So*AgentPlugindirectly satisfiessdk.PluginProvider— nointernal/grpc_adapter.gowrapper is needed.agent.New()is passed straight toServePluginFull. This is the minimal correct shape.Evidence (real output)
Build + test green:
P6 gate —
wfctl plugin verify-capabilities(wfctl built from workflow origin/maine6ad6b7d6):Runtime
GetManifestmatches plugin.json exactly: 4 steps (step.agent_execute,step.provider_test,step.provider_models,step.model_pull) +agent.providermodule.Goreleaser dry-run (
goreleaser release --snapshot --clean, EXIT 0):workflow-plugin-agentbinary (+.exeon windows) +plugin.json+plugin.contracts.json+LICENSE.checksums.txtwith SHA-256 for all 6 archives.beforehook correctly rewrote plugin.json version + URLs (verified via snapshot mutation).Scope
Task 1 only. Does NOT touch
orchestrator/(Phase 2), ratchet, registry, or other repos. Does NOT bumpminEngineVersion. Does NOT tag or merge (lead verifies + merges).🤖 Generated with Claude Code