Skip to content

fix(run): a composition run's roots leave out what the environment withdraws - #1204

Merged
antoinetoussaint-byte merged 2 commits into
mainfrom
run/roots-honour-exclusions
Oct 27, 2026
Merged

antoinetoussaint-byte merged 2 commits into
mainfrom
run/roots-honour-exclusions

Conversation

@antoinetoussaint-byte

@antoinetoussaint-byte antoinetoussaint-byte commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What we are trying to do

Let codefly run workspace run a composition whose environment withdraws services with exclude-services — the mechanism #1195 made whole-module. obin-ai/platform-obin's local now withdraws the codefly-dev/authority module and the runtime's admission witness exactly as its hosted environments do, and the run refused at ordering:

cannot initialize service saas/marketing: runService: flow.InitManagers: cannot order services: cannot topologically sort to <runtime/admission-witness>: service runtime/admission-witness is not in the graph

Why

CompositionRoots (cmd/run/workspace.go) took every service of every composed module as a root and consulted only --exclude-dependency and the run profile. The flow builds the dependency graph without a withdrawn service (architecture.ExcludeServices, from env.ExcludedServiceUniques() in pkg/orchestration/flow.go), so a root naming one is a sort target the graph does not hold. Every other per-environment loop over a module's services — render, build, deploy — reads env.ComposedServices(module); this one did not.

Change

CompositionRoots reads env.ComposedServices, skips a module the environment withdraws whole (env.WithdrawsModule), and does not seed a withdrawn service-entry. The flag's and the run profile's exclusions still apply on top, unchanged.

Evidence

  • TestCompositionRootsLeaveOutWhatTheEnvironmentWithdraws: an environment withdrawing one service of a module and a second module whole yields roots of the remaining services only; with no exclusions every service is a root as before. Fails without the change (asserts [shop/api shop/cache], gets the withdrawn ones too) and passes with it.
  • go test ./cmd/run, golangci-lint run ./cmd/run/...: clean.
  • On platform-obin local with the authority module withdrawn, a CLI built from this branch gets past flow.InitManagers where main refuses with the line above.

Classification: fix, at the owner of the roots.

🤖 Generated with Claude Code

Docs and regression (added on review)

docs/commands.md, under codefly run workspace, now states that the selected environment's exclude-services removes services from the composition roots (a wholly withdrawn module contributes none, its service-entry included) and that CLI and run-profile exclusions still apply on top. cmd/run/workspace_environment_exclusions_test.go orders every selected root through the real Core graph built with the same environment filter the flow uses — red on the pre-fix CompositionRoots, green here. Both from the independent review's patch, adopted after reading.

…thdraws

`codefly run workspace` took every service of every composed module as a root
and consulted only --exclude-dependency and the run profile, never the
environment's exclude-services. The dependency graph is built without a
withdrawn service (architecture.ExcludeServices), so a root naming one was a
sort target the graph did not hold, and the run refused at ordering:

    cannot initialize service saas/marketing: runService: flow.InitManagers:
    cannot order services: cannot topologically sort to
    <runtime/admission-witness>: service runtime/admission-witness is not in
    the graph

Seen on obin-ai/platform-obin, whose `local` now withdraws the authority module
and the runtime's admission witness exactly as its hosted environments do.

CompositionRoots now reads env.ComposedServices — the one list every loop over a
module's services for one environment reads — skips a module the environment
withdraws whole, and does not seed a withdrawn service-entry. A run profile's
and the flag's exclusions still apply on top.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…vices; regression orders every root

The standing rule: a behaviour change updates docs/ in the same PR.
`docs/commands.md` now says, under `codefly run workspace`, that the selected
environment's `exclude-services` removes services from the composition roots —
a module withdrawn whole contributes none, its service-entry included — and
that CLI and run-profile exclusions still apply on top.

The regression builds an isolated two-module workspace, selects `local` (which
withdraws worker/witness) and `staging` (which does not), applies CLI and
profile exclusions beside them, calls the real CompositionRoots, builds the
real Core dependency graph with the same selected-environment filter the flow
uses, and orders to every selected root — the exact shape that failed on main:
`cannot topologically sort to <worker/witness>: service worker/witness is not
in the graph`. Red on the pre-fix CompositionRoots, green on this branch
(independent review, codex-cli1204-exclusion-review).

Patch authored by the Codex independent reviewer; adopted unchanged after
reading it, with `go test ./cmd/run/` and golangci-lint clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit 102413a Oct 27, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant