From e3d47cfb41409ccd44617b47d5e9a210008862e7 Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:30:06 -0400 Subject: [PATCH 1/3] fix(generate): runnable-bindings materializes pinned modules, so --check can run on a fresh checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `codefly generate runnable-bindings` walks every composed module for its derived operations, but loaded the workspace with the plain loader. On a composition of pinned modules with no codefly.local.yaml — a CI job, or any fresh checkout — it refused before reading anything: module resolves to a pinned artifact (source "obin-ai/core-solutions", version "0a18964f…"); pinned modules are pulled by the CLI, not loadable as a local checkout so `--check`, the one drift check this generated group has, could not be run where drift is caught. The command now materializes pinned modules first, exactly as `codefly run`, `deploy gitops render` and `generate fixture-composition` do (common.LoadWorkspaceWithPinnedModules), and is free once the workspace is materialized. Verified on obin-ai/platform-obin main (694ac3f4), environment blue: before, the refusal above; after, the check reaches the regeneration and refuses with the composition's real contradiction — `--call-endpoint "saas/auth-gateway/hop": service saas/auth-gateway declares no endpoint hop (it declares [grpc rest workload])` — while the committed configurations/blue/runnable-bindings.env binds 17 operations to their owners' direct in-cluster addresses, two of them on module compute, which the composition does not compose. First slice of making render and deploy refuse committed generated output that no longer matches what the generator would produce; the generic check on the readiness gate follows (see the handoff). Co-Authored-By: Claude Fable 5.1 --- cmd/generate/runnable_bindings.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/generate/runnable_bindings.go b/cmd/generate/runnable_bindings.go index f6a376a0..b5b1c4c2 100644 --- a/cmd/generate/runnable_bindings.go +++ b/cmd/generate/runnable_bindings.go @@ -141,7 +141,12 @@ Examples: ctx, stop := common.SignalContext(ctx) defer stop() - workspace, err := common.LoadWorkspace(ctx) + // Every composed module is walked for its derived operations, so a + // pinned module is materialized first, as a run, a render and + // `generate fixture-composition` do: on a fresh checkout — a CI job — + // the plain loader refuses a pinned module as "not loadable as a local + // checkout", and --check never reaches the file it exists to judge. + workspace, err := common.LoadWorkspaceWithPinnedModules(ctx) if err != nil { return err } From 2b0d3306253420b19d20f614e3824919f9e2ae0d Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:41:43 -0400 Subject: [PATCH 2/3] docs(generate): --check writes no binding output; materializing a pinned module is not writing output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The review's finding: `docs/commands.md` promised `--check` "writes nothing", and the loader now materializes a pinned module first — dependency cache, overlay, receipt and `.gitignore` entries — as every command that reads composed modules does. The promise is narrowed to what it always meant, the binding output, in the flag table, the flag's own help (and so `docs/cli-reference.md`, regenerated), and `docs/runnable.md`'s "a failed preparation writes nothing"; `generate runnable-bindings` and `generate fixture-composition` join the "Which commands materialize" list. Co-Authored-By: Claude Fable 5.1 --- cmd/generate/runnable_bindings.go | 2 +- docs/cli-reference.md | 2 +- docs/commands.md | 8 +++++--- docs/runnable.md | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/generate/runnable_bindings.go b/cmd/generate/runnable_bindings.go index b5b1c4c2..2f6e8a70 100644 --- a/cmd/generate/runnable_bindings.go +++ b/cmd/generate/runnable_bindings.go @@ -205,7 +205,7 @@ Examples: func init() { RunnableBindingsCmd.Flags().StringVar(&runnableBindingsEnv, "env", "local", "environment supplying resolved addresses and runnable-scope-selections") - RunnableBindingsCmd.Flags().BoolVar(&runnableBindingsCheck, "check", false, "do not write; exit 1 if the on-disk file differs from what would be generated") + RunnableBindingsCmd.Flags().BoolVar(&runnableBindingsCheck, "check", false, "write no binding output; exit 1 if the on-disk file differs from what would be generated (pinned modules are still materialized, as for every command that reads them)") RunnableBindingsCmd.Flags().StringVar(&runnableBindingsNamingScope, "naming-scope", "", "`scope` addresses resolve in, as codefly run --naming-scope reads it: omitted keeps the environment's, empty clears it") RunnableBindingsCmd.Flags().StringVar(&runnableBindingsCallEndpoint, "call-endpoint", "", "declared HTTP-based endpoint, as `module/service/endpoint`, every prepared call is sent to instead of the owner's listener, overriding the environment's runnable-call-endpoint (empty: each owner's listener; the owner's operation, route, contract and policy are kept)") } diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 45bdc9f4..bcbf420b 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -3036,7 +3036,7 @@ Flags: ``` --call-endpoint module/service/endpoint declared HTTP-based endpoint, as module/service/endpoint, every prepared call is sent to instead of the owner's listener, overriding the environment's runnable-call-endpoint (empty: each owner's listener; the owner's operation, route, contract and policy are kept) - --check do not write; exit 1 if the on-disk file differs from what would be generated + --check write no binding output; exit 1 if the on-disk file differs from what would be generated (pinned modules are still materialized, as for every command that reads them) --env string environment supplying resolved addresses and runnable-scope-selections (default "local") --naming-scope scope scope addresses resolve in, as codefly run --naming-scope reads it: omitted keeps the environment's, empty clears it ``` diff --git a/docs/commands.md b/docs/commands.md index 42dd9b0c..5846b339 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -2196,8 +2196,10 @@ workspace composed by identity works for each of them with no prior run: checkout, so it materializes exactly as `run` does; - every `ci` verb (`plan`, `build`, `test`, `run`, `validate`, `push`, `deploy`); -- `generate client`, `sync solution-sdk` and the fixture listing, which read a - composed module's tree. +- `generate client`, `generate runnable-bindings` (whose `--check` is the one + drift check that group has, and could not run on a fresh checkout until it + materialized), `generate fixture-composition`, `sync solution-sdk` and the + fixture listing, which read a composed module's tree. All of them share the fast path: once a receipt answers the request the workspace makes, a later command compares and pulls nothing, takes no lock and @@ -3902,7 +3904,7 @@ absent or empty exactly when the environment selects no binding. | Flag | Description | |------|-------------| | `--env` | Environment supplying resolved addresses and `runnable-scope-selections` (default: `local`) | -| `--check` | Regenerate in memory; exit non-zero if the output is stale, including after a selection or a decline changes; write nothing | +| `--check` | Regenerate in memory; exit non-zero if the output is stale, including after a selection or a decline changes; writes no binding output. Like every command that reads composed modules, it materializes a pinned module first — cache, overlay, receipt and `.gitignore` entries — so it runs on a fresh checkout | ## Infrastructure diff --git a/docs/runnable.md b/docs/runnable.md index 40a3eae2..e1e92fb5 100644 --- a/docs/runnable.md +++ b/docs/runnable.md @@ -145,7 +145,9 @@ declaring neither fixed scopes nor slots is still refused as declaring no authority. Wildcards (including omitted IDs), collisions with a fixed scope's kind or another slot's kind, duplicate/undeclared slots, unknown fields and selection keys naming no derived operation are refused. A failed preparation -writes nothing and preserves the previous output. +writes no binding output and preserves the previous output; a pinned module it +materialized first stays materialized (cache, overlay, receipt, `.gitignore`), +as after any command that reads composed modules. The generated `configurations//runnable-bindings.env` carries Core's `codefly.runnable-prepared/v3` bindings: owner coordinates, the resolved JSON From d4de74c58b737e72659dc1d5823787b86c0773d9 Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:42:16 -0400 Subject: [PATCH 3/3] =?UTF-8?q?docs(generate):=20keep=20only=20what=20#120?= =?UTF-8?q?9=20does=20not=20carry=20=E2=80=94=20the=20materialize=20list?= =?UTF-8?q?=20and=20runnable.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit e9cf1cf8 (#1209) already corrects the --check flag row, the flag help and docs/cli-reference.md; this PR keeps the two items the review left to it. Co-Authored-By: Claude Fable 5.1 --- cmd/generate/runnable_bindings.go | 2 +- docs/cli-reference.md | 2 +- docs/commands.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/generate/runnable_bindings.go b/cmd/generate/runnable_bindings.go index 2f6e8a70..b5b1c4c2 100644 --- a/cmd/generate/runnable_bindings.go +++ b/cmd/generate/runnable_bindings.go @@ -205,7 +205,7 @@ Examples: func init() { RunnableBindingsCmd.Flags().StringVar(&runnableBindingsEnv, "env", "local", "environment supplying resolved addresses and runnable-scope-selections") - RunnableBindingsCmd.Flags().BoolVar(&runnableBindingsCheck, "check", false, "write no binding output; exit 1 if the on-disk file differs from what would be generated (pinned modules are still materialized, as for every command that reads them)") + RunnableBindingsCmd.Flags().BoolVar(&runnableBindingsCheck, "check", false, "do not write; exit 1 if the on-disk file differs from what would be generated") RunnableBindingsCmd.Flags().StringVar(&runnableBindingsNamingScope, "naming-scope", "", "`scope` addresses resolve in, as codefly run --naming-scope reads it: omitted keeps the environment's, empty clears it") RunnableBindingsCmd.Flags().StringVar(&runnableBindingsCallEndpoint, "call-endpoint", "", "declared HTTP-based endpoint, as `module/service/endpoint`, every prepared call is sent to instead of the owner's listener, overriding the environment's runnable-call-endpoint (empty: each owner's listener; the owner's operation, route, contract and policy are kept)") } diff --git a/docs/cli-reference.md b/docs/cli-reference.md index bcbf420b..45bdc9f4 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -3036,7 +3036,7 @@ Flags: ``` --call-endpoint module/service/endpoint declared HTTP-based endpoint, as module/service/endpoint, every prepared call is sent to instead of the owner's listener, overriding the environment's runnable-call-endpoint (empty: each owner's listener; the owner's operation, route, contract and policy are kept) - --check write no binding output; exit 1 if the on-disk file differs from what would be generated (pinned modules are still materialized, as for every command that reads them) + --check do not write; exit 1 if the on-disk file differs from what would be generated --env string environment supplying resolved addresses and runnable-scope-selections (default "local") --naming-scope scope scope addresses resolve in, as codefly run --naming-scope reads it: omitted keeps the environment's, empty clears it ``` diff --git a/docs/commands.md b/docs/commands.md index 5846b339..5ffa08fe 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -3904,7 +3904,7 @@ absent or empty exactly when the environment selects no binding. | Flag | Description | |------|-------------| | `--env` | Environment supplying resolved addresses and `runnable-scope-selections` (default: `local`) | -| `--check` | Regenerate in memory; exit non-zero if the output is stale, including after a selection or a decline changes; writes no binding output. Like every command that reads composed modules, it materializes a pinned module first — cache, overlay, receipt and `.gitignore` entries — so it runs on a fresh checkout | +| `--check` | Regenerate in memory; exit non-zero if the output is stale, including after a selection or a decline changes; write nothing | ## Infrastructure