Repository navigation
fix(orchestration): name the agent behind a "requires host resolution" refusal - #1291
Merged
antoinetoussaint-byte merged 2 commits intoNov 4, 2026
Merged
Conversation
…" refusal `codefly deploy gitops render … --env blue` on a workspace that imports another by `source:` + `version:` fails with cannot bind interface dependencies: resources.ApplyInterfaceBindings: cannot load the workspace that binds the service's interfaces: LoadFromDir: workspace "platform-core" at obin-ai/platform-core@5a8d5ffa… requires host resolution and every operator has answered it with an uncommitted `path:` override on the import. The host is not what is missing. The CLI registers a workspace resolver on every command context and resolves the import through the module cache; the composition loads and binds the service; core's services.Load hands the agent that binding (ServiceIdentity.interface_bindings, core v0.19.8). The sentence comes from the AGENT process: an agent built on a core older than v0.19.8 ignores the field, loads the workspace at workspace_path itself, and cannot compose the import because it registers no resolver. The `go` agent every solution runs on is at core v0.19.5. The CLI now says so. When an agent's Load refusal carries core's sentence and this host did supply the binding, the error names the agent (the running selection, else the declared one), what it did instead of reading the binding, the release floor that reads it, and that a `path:` override hides this without fixing it; the agent's own error stays wrapped underneath. A service nothing bound — loaded outside its composition — keeps the agent's refusal as it stands, because binding against the workspace on disk is then the contract. Applied at the three places the orchestration loads an agent's builder or runtime. Tests lock both halves: the explained error for a bound service and the untouched error for an unbound one or any other failure; and, in pkg/composition, a workspace importing another by source and commit pin with the module cache populated loads through the resolver with no `path:` override, and the service it composes carries the binding the host hands its agent, while the same load without the resolver is core's refusal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…carrying it 4ab16f55c (2026-10-25) is in v0.19.7 (2026-10-27); v0.19.6 predates it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
antoinetoussaint-byte
enabled auto-merge
November 4, 2026 04:48
This was referenced Nov 4, 2026
Closed
Closed
Closed
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.
Problem
codefly deploy gitops render <module> --env blueon obin-ai/platform-obin, which imports platform-core asworkspaces: - source: obin-ai/platform-core, version: <sha>, refuses with:so every Blue render has needed an uncommitted
path:override pointing at~/.codefly/modules/obin-ai/platform-core/<sha>— a directory the CLI itself had already fetched.Diagnosis (verified in source)
The host is not what is missing:
composition.ResolveWorkspaceReferenceon every command context (cmd/root.go,cmd/common/context.go) and it resolves asource:+version:import through the same module cache a module pin uses (ensurePinnedArtifact). The composition loads;workspace.LoadModuleFromName→module.LoadServiceFromNamebinds the service's interface dependencies.services.Loadalready hands the agent that binding:ServiceIdentity.interface_bindings(field 7, core 4ab16f55c, 2026-10-25, in every core tag ≥ v0.19.7 (v0.19.6 predates it)), filled fromService.InterfaceBindingsProto()in both the builder and the runtimeLoadRequest. The CLI has pinned a core carrying it since v0.1.213 (core pseudo-version 20261025185759-e501458995d0).agents/services/base.go,cannot bind interface dependencies). An agent built on a core older than v0.19.7 ignores field 7, callsApplyInterfaceBindingsagainstworkspace_pathitself, and cannot compose the import because it registers no resolver. The services that declareinterface:dependencies in the obin composition (wiki/backend, wiki/publisher, proposals/backend, robin-builder/backend) run on thegoagent at0.0.67; its latest releasev0.0.74is on core v0.19.5. Onlygo-grpc(v0.1.72) is on a core that reads the binding.So the real fix is an agent release built on core ≥ v0.19.7 and a pin bump in the composition; a CLI change cannot make an old agent read a field it does not know, and the only lever on it (materialising a rewritten workspace file for the agent) is the hand
path:override automated — refused.Change (classification: fix, at the place in this repo that owns the behaviour)
The CLI owns what it tells the operator about a peer it loaded. When an agent's Load refusal carries core's
requires host resolutionand this host did supply the binding (instance.Service.InterfaceBindingsProto() != nil), the error now names the agent (the running selection, else the declared one), what it did instead of reading the binding, the release floor (core v0.19.7 or newer), and that apath:override hides this without fixing it. The agent's own error stays wrapped underneath. A service nothing bound — loaded outside its composition — keeps the agent's refusal as it stands, since binding against the workspace on disk is then the contract. Applied at the three places the orchestration loads an agent's builder or runtime (builder.go,runner.go,local_runtime_image.go), through one helper:pkg/orchestration/interface_binding_refusal.go.Docs: a paragraph under What an agent must report in
docs/agent-compatibility.mdrecords the host/agent binding boundary and the floor.Reuse
Reused core's field 7 and
Service.InterfaceBindingsProto/services.Load(nothing added on the host side — it already supplies the binding), the existing resolver registration, andmodulecache.Envfor the test's cache root. Deliberately not built: a second binding path, a capability probe (core's agent contract has no capability for the supplied binding, so the refusal can only be recognised after Load), or any rewrite of the workspace the agent reads.Tests
pkg/orchestration/interface_binding_refusal_test.go— a bound service + the Blue error yields the explained error (namescodefly.dev/go:0.0.67, then the running0.0.74when a selection resolved;errors.Isthe original); an unbound service, an unrelated error, nil error, nil instance are returned untouched. Fails before (helper absent), passes after.pkg/composition/workspace_reference_test.go— a product workspace importingobin-ai/platform-coreby source + commit pin, with the module cache (CODEFLY_MODULE_CACHE) populated at that pin and nopath:override, loads throughWithWorkspaceResolver, and the service it composes carries the binding toplatform/apigrpc; the same load without the resolver is core'srequires host resolution. This one passes onmaintoo: it locks the host-side contract the diagnosis rests on, so a regression there would read as this bug again.go build ./...,go vet,golangci-lint run(0 issues) and the fullpkg/orchestration+pkg/compositionsuites pass.Not verified
goagent (and any other agent serving a service withinterface:dependencies) is released on core ≥ v0.19.7 and the composition's pin moves.Would file (not opened, per the brief)
interface:dependencies hit this).service-interface-bindings/v1) so the host can refuse before Load, per "compatibility is runtime-only"; and letServiceIdentity.Proto()take the bindings so no host path builds the proto without them.path:override once the agent pin moves.🤖 Generated with Claude Code