Repository navigation
fix(sync): solution-sdk generates a working client for a solution consuming modules through the authority hop (#1094) - #1095
Merged
antoinetoussaint-byte merged 2 commits intoOct 27, 2026
Conversation
…suming modules through the host gateway (#1094) Four gaps stopped `codefly sync solution-sdk` on any solution that names no executor and consumes modules over the transports the host gateway serves: - The manifest is read with core's LoadConsumption, the loader core keeps for a reader of api.consumes alone: a solution with no `agent` block is no longer refused for lacking a codefly:solution executor nothing runs. - A consume of a connect or rest endpoint resolves to its service's protobuf contract, which a catalog exports under grpc. - Only a consume of the solution's own module asks for (or, with --apply-dependencies, gets) a service-dependencies edge; another module is reached through the host gateway, and a direct edge is the mesh path a solution must not hold. - The aggregated library gives each facade a package of its own (Go go/<facade>/ with bindings under gen/<facade>/; TypeScript src/<facade>/ exported as <package>/<facade>, the root re-exporting the facades alone), and refuses two entries generating one facade name. Covered by a fixture in cmd/sync/testdata/solution-sdk and a test per gap; the Docker end-to-end builds the Go half, writes the TypeScript half and passes --check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
antoinetoussaint-byte
force-pushed
the
issue-1094-sync-solution-sdk-cannot-generate-a-solution-s
branch
from
October 21, 2026 18:30
70c1c81 to
b5f6c99
Compare
…through the authority hop (#1094) Brings the branch up to main (72 commits) and adapts it to the hop: - Gap 3: a consume of another module needs no service-dependencies entry because the solution calls it through the authority hop's Connect face, which routes each module behind it from its published API catalog and checks every call against the method's policy; an edge to the module would be a mesh path past the hop. The rule is unchanged; its reason, the flag help and the docs no longer name the host gateway's /v1/<alias> route. - Gap 2: several protobuf entries of one service under one digest are one contract, as the hop routes a service's gRPC and Connect listeners under one contract as one destination; different digests are still refused. - Gap 4 on main's staged generation (#1163) and namespaced TypeScript barrel (#1162): Go keeps a package per facade; TypeScript keeps a bindings directory per entry (src/gen/<facade>/) under main's entry point, which already keeps two contracts' same-named messages apart, so the per-facade TypeScript index and the package.json "./*" subpath export are dropped. Co-Authored-By: Claude Opus 5.5 <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.
Closes #1094.
codefly sync solution-sdk --language go,typescriptnow generates a working library for a solution that has noagentblock and consumes two composed modules, one overconnectand one overrest, with noservice-dependencies. The Go half builds, the TypeScript half is written, and--checkpasses on the result.Updated 2026-10-27: adapted to the authority hop, merged up to main
This branch was 72 commits behind and conflicted in four files. Main has been merged in (97be7c4). There was no rebase and no force-push. Three things changed against the first version:
/v1/<alias>route. The hop routes every module the composition puts behind it (hop/MODULES) from that module's published API catalog, and checks each call against the method's policy. The rule stays the same: noservice-dependenciesedge for another module's consume. The reason is new: such an edge is a mesh path past the hop, a call no policy is checked on. The code comment, the flag help, the long help,docs/commands.md,docs/design/library-system.mdand the tests no longer mention the gateway. Test renamed toTestSyncSolutionSDKAsksNoMeshEdgeForAHopRoutedConsume.src/<facade>/index.tsand thepackage.json"./*"subpath export were working around, so both are dropped. Two mechanisms for one job is one too many. What stays is the part main does not cover: each entry's bindings get their own directory,src/gen/<facade>/, because two contracts rooted atapi.protowould otherwise write the samesrc/gen/api_pb.ts. The Go half is unchanged: a package per facade,go/<facade>/overgo/gen/<facade>/. It is re-threaded through main's staged generation (generate client --language python: buf/validate unresolved, and a failed run deletes the library's python tree #1163) asstagedLanguage.facadePackages.The generated client is still the Codefly-generated Connect client of each consumed contract. There is no hand-written REST path.
The four gaps
1. A solution manifest without an
agentblock (fix, in the CLI; no core change)runSyncSolutionSDKused to readsolution.codefly.yamlwith core'smanifest.Load. That is the executor manifest loader: it requiresagent.kind == codefly:solution, and the manifest's digest and admission bind that identity. This command reads onlyapi.consumes, so it now uses core'smanifest.LoadConsumption, the loader core provides for such a reader (pkg/gitops/authority.goalready uses it).schema_versionandapi.consumesare still held strictly.2. A consume of a
restorconnectendpoint (fix)consumedContractgenerates a consume from the protobuf contract the hop routes for the consumed service:restorconnectendpoint uses the service's protobuf contract. Several entries under one digest are one contract; different digests are refused.restendpoint of a service with no protobuf contract keeps its own OpenAPI entry.3. The dependency check and mesh edges (fix)
missingServiceDependenciesreadsapi.consumesand asks for an edge only for a consume of the solution's own module. A consume of any other module is called through the authority hop, so neither--checknor--apply-dependenciesasks for or adds an edge to it. Existing edges are never removed.4. The aggregated library with two modules (fix)
ClientLibraryRequest.FacadePackages(set only bysync solution-sdk) works like this:go/<facade>/<facade>_facade.pb.go, imported as<module path>/<facade>, with bindings undergo/gen/<facade>/. Each Go facade has its own package clause, so two cannot sharego/, and two contracts rooted atapi.protowould write the same binding.src/gen/<facade>/, and the facade stays atsrc/<facade>_facade.ts. The entry point is main's namespaced barrel, unchanged.ason one").generate client(one contract) keeps its layout byte for byte.Reused, and deliberately not built
manifest.LoadConsumption; main's stagedGenerateClientLibrarypipeline; main's namespaced TypeScript barrel (writeTypeScriptIndex, fix(generate): namespace TypeScript client barrel exports #1162), in place of this PR's earlier per-facade TypeScript index and subpath export.hop/MODULES, since that is the environment's configuration and the hop refuses an unrouted call itself.Tests (each confirmed failing with its fix reverted)
TestSyncSolutionSDKReadsAManifestWithoutAnAgent.TestSyncSolutionSDKResolvesTransportEndpointsToTheProtobufContract. It now also covers a publishedconnectentry besidegrpc, arestconsume over one contract on both, and two digests refused.TestSyncSolutionSDKAsksNoMeshEdgeForAHopRoutedConsumeandTestSyncSolutionSDKApplyDependencies.TestAggregatedGoLibraryGivesEachFacadeItsOwnPackage,TestAggregatedLibraryRefusesTwoEntriesWithOneFacadeName, and the newTestAggregatedTypeScriptLibraryGivesEachEntryItsOwnBindings. In onesrc/gen/the secondapi_pb.tsreplaces the first. Undersrc/gen/<facade>/both stay, and the root barrel namespacesaccounts_v1_pbandrecords_v1_pband lists the sharedGetRequestSchemain its collision comment. Confirmed failing with the bindings directory reverted.TestSyncSolutionSDKTwoModulesEndToEnd: real proto companion,--language go,typescript, Go compiled by the command, then--checkpasses. Run locally on the merged head (darwin/arm64, Docker 29.8.2,CODEFLY_GENERATE_QUALIFY=1): PASS in 50 s.go vet ./cmd/... ./pkg/generators/...clean;golangci-lint run ./cmd/sync/... ./pkg/generators/...0 issues;go test ./cmd/sync/... ./pkg/generators/... ./cmdpass (docs/cli-reference.mdregenerated).Breaking changes
sync solution-sdk --apply-dependenciesand--checkno longer add or requireservice-dependenciesfor consumes of other modules. Those edges are mesh paths past the hop.sync solution-sdklibrary layout changes. Go facades move fromgo/togo/<facade>/(import path<module path>/<facade>), and bindings fromgo/gen/togo/gen/<facade>/. TypeScript bindings move fromsrc/gen/tosrc/gen/<facade>/; the root barrel still exports every facade and every package. A previously generated solution SDK fails--checkuntil it is regenerated. The earlierpackage.json"./*"hand-edit is no longer needed.generate clientoutput is unchanged.Not verified
${endpoint:authority/hop/http}in a composed run here.tscwas not run on the generated TypeScript of the merged head. The end-to-end test checks the files exist, and main's barrel has its owntsc-backed tests.generate contractsexport./v1/<alias>routes and gateway mesh edges fromapi.consumes(orchestration.HostRouteTargets, used bypkg/gitops/cell.goandmesh_edges.go). This PR does not touch them. Whether they are now dead under the hop is a separate question for that code's owner.Overlaps
path.JoininmergeTypeScriptEntry); the per-entry bindings prefix goes through the samepath.Join.solution/manifestpackage doc still says the CLI derives runtimeservice-dependenciesfromapi.consumes. That sentence is stale. It belongs to core's owner, and no core issue was filed.Docs
docs/commands.md(sync solution-sdk: loader, hop-based dependency rule, transport and one-digest resolution, library layout),docs/design/library-system.md(Solution SDKs: the declaration does not driveservice-dependenciesbecause of the hop),docs/cli-reference.md(regenerated).🤖 Generated with Claude Code