fix(agents): gate SWARM/hybrid transfer worker registration behind requiredWorkers; fix sub-agent tool domain routing - #459
Open
kowser-orkes wants to merge 3 commits into
Conversation
…rkers
_register_swarm_transfer_workers and _register_hybrid_transfer_workers
unconditionally registered (and PUT-taskdef'd) a worker for every
{source}_transfer_to_{peer} name, bypassing the _server_needs gate every
other system-worker registration in _register_workers respects. These
names are compiler-owned control signals (toolType="handoff", INLINE
after the paired conductor-oss fix) that are never dispatched as real
tasks -- the unconditional PUT is exactly what 404s against a server
that (correctly) never created a TaskDef for them (conductor-oss #1363).
- both functions take an optional server_needs predicate and skip
worker_task(...) per-name when the server doesn't list it; None
preserves the old register-everything fallback for older servers
- drop the "always register anyway" bypass comments -- the nested/
sub-workflow gap they cited doesn't apply to this name category,
since _collect_worker_names already recurses independently of the
server for genuinely-declared @tool workers
- new unit tests: per-name gating (registers only what's needed),
full-skip when server needs nothing, and server_needs=None fallback
parity with prior behavior
ToolRegistry.register_tool_workers() only honored *domain* when the *current* agent (or the individual tool) was itself marked stateful=True. For a SWARM/hybrid/sequential parent whose sub-agents own their own @tool workers, each sub-agent's own .stateful is False even though the whole compiled graph is domain-routed once any agent in the tree is stateful -- confirmed server-side, AgentConfig.java's collectDeclaredWorkerTaskNames() puts every "worker"-type tool name into taskToDomain unconditionally whenever runId is set, with no per-tool gate. The client's extra local re-check left sub-agent tool workers polling the domain-less queue while their tasks sat in the domain-scoped queue forever -- a live stateful SWARM run hung on the very first tool call past the conductor-oss #1363 fix. domain is already None unless AgentRuntime._resolve_worker_domain / _has_stateful_tools determined (once, tree-wide) that this execution is stateful, so re-deriving it locally was redundant and wrong for nested agents. Drop the agent_stateful param entirely and always pass domain through. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ARM transfer-worker registration fix Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests.
🚀 New features to boost your workflow:
|
ling-senpeng13
added a commit
to conductor-oss/conductor
that referenced
this pull request
Jul 30, 2026
The entries had grown into full root-cause write-ups — 2.2KB for one reason string. That analysis belongs in the tracking issue and the fixing PR, where it can be discussed and closed out; duplicated here it just goes stale silently, which is exactly what happened to the suite14 entry. Each value is now a short statement of what fails plus a tracking link. _README says to keep it that way. File is 2227 bytes, down from 4781. Kept the operational parts, which are not history and prevent real mistakes: the E2E_MIN_PASSED coupling, and the warning that a run:false xfail can never XPASS so it must be un-listed by hand. Also repoints suite14 at conductor-oss/python-sdk#459, which supersedes the now-closed #455. Verified: plugin still reports both entries matching exactly one test, no MATCHED NOTHING. Co-Authored-By: Claude Opus 5 (1M context) <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.
Pull Request type
Changes in this PR
Bug: a stateful
Strategy.SWARMagent never reaches a terminal state against conductor-osspast rc.16 (#1363). Two independent causes:
_register_swarm_transfer_workers/_register_hybrid_transfer_workersalways registered aTaskDeffor every{source}_transfer_to_{peer}name, ignoring therequiredWorkersgate everyother system-worker registration respects. These names are compiler-owned control signals, never
real dispatched work — the
PUT /api/metadata/taskdefs404s.ToolRegistry.register_tool_workersonly applied the resolved workerdomainwhen thecurrent (sub-)agent itself was
stateful=True. The server domain-routes every tool in thewhole tree once any agent is stateful, so a SWARM member's real
@tooltask lands in thedomain-scoped queue while its own worker polls the domain-less queue — task sits
SCHEDULEDforever. This is the actual hang.
Fix:
requiredWorkers(per-name, via aserver_needspredicate);server_needs=Nonekeeps the old register-everything fallback forservers that don't return
requiredWorkers.agent_statefulre-check inregister_tool_workers; always pass thealready tree-wide-resolved
domainthrough.server_needs=Nonefallback parity, domain-routing pin.Validated: full unit suite green (2613 passed, 1 skipped, 0 failed). Live-validated the
registration gate without an LLM (real
/api/agent/deploycall, zero registration attempts for_transfer_to_names). Live LLM re-run of the repro against the domain fix is the one open item.Paired with: conductor-oss PR mapping
toolType="handoff"→INLINE. Fixing either alonedoesn't fully close #1363.
Issue #1363
Alternatives considered
server list some peer-pair transfer names as needed and not others; a single call-site check
can't express that.
task_runner.py'sPUT→POSTfallback to paper over the bad registration — rejected:that's the existing non-stateful behavior and doesn't explain the stateful-specific hang; better
to stop the wasted call entirely.