fix-up(#4027): narrow coordinator guard to registry hard-reject set
Maintainer feedback (#4071 review): the original guard rejected sisyphus and atlas as subagent targets even from team-mode where resolveMember() intentionally calls resolveSubagentExecution with allowPrimaryAgentDelegation: true. Per AGENT_ELIGIBILITY_REGISTRY (src/features/team-mode/types.ts), only prometheus is hard-reject; sisyphus and atlas are explicitly verdict: 'eligible' for team membership. Shrink COORDINATOR_AGENT_NAMES to ['prometheus'] so the guard aligns with the registry's authoritative classification, document the scoping rule in a comment, and add regression tests covering: - sisyphus is NOT blocked by the coordinator guard (registry eligible) - atlas is NOT blocked by the coordinator guard (registry eligible) - prometheus IS blocked even when allowPrimaryAgentDelegation: true (registry hard-reject is authoritative) Fixes the 5 zauc-mocks resolver tests that were locking in the wrong rejection set (including 'allows delegating to a primary agent when allowPrimaryAgentDelegation is enabled'). The one test asserting the literal primary-agent error string for Prometheus display-name was loosened to a regex that accepts either guard's message, since prometheus is now caught by the coordinator path which fires before the primary-agent lookup.
This commit is contained in:
@@ -352,9 +352,15 @@ export function isPlanFamily(category: string | undefined): boolean {
|
||||
* arbitrary subagent targets via task(). Delegating to these creates duplicate
|
||||
* orchestration and conflicting team state (issue #4027).
|
||||
*
|
||||
* Scoped to AGENT_ELIGIBILITY_REGISTRY hard-reject entries only — sisyphus and atlas
|
||||
* are explicitly marked `verdict: "eligible"` for team membership in the registry
|
||||
* (src/features/team-mode/types.ts), so they are NOT included here. Adding them would
|
||||
* conflict with the team-mode resolver's intentional `allowPrimaryAgentDelegation: true`
|
||||
* opt-in.
|
||||
*
|
||||
* Symmetric guard to the caller-eligibility check added by PR #4065 for team_create.
|
||||
*/
|
||||
export const COORDINATOR_AGENT_NAMES = ["prometheus", "atlas", "sisyphus"]
|
||||
export const COORDINATOR_AGENT_NAMES = ["prometheus"]
|
||||
|
||||
/**
|
||||
* Returns true when the given agent name refers to a coordinator/meta agent that
|
||||
|
||||
@@ -30,6 +30,7 @@ describe("coordinator subagent guard (#4027)", () => {
|
||||
prompt: "do something",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -51,6 +52,7 @@ describe("coordinator subagent guard (#4027)", () => {
|
||||
prompt: "plan something",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -72,6 +74,7 @@ describe("coordinator subagent guard (#4027)", () => {
|
||||
prompt: "write some code",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
@@ -80,6 +83,62 @@ describe("coordinator subagent guard (#4027)", () => {
|
||||
//#then — hephaestus may fail for other reasons (API call), but NOT the coordinator guard
|
||||
expect(result.error).not.toContain("coordinator agent")
|
||||
})
|
||||
|
||||
test("#given subagent_type=sisyphus #when resolveSubagentExecution is called #then sisyphus is NOT blocked by coordinator guard (registry: eligible)", async () => {
|
||||
//#given — sisyphus is verdict:'eligible' in AGENT_ELIGIBILITY_REGISTRY; it must not be rejected by the coordinator guard
|
||||
const ctx = makeCtx()
|
||||
const args = {
|
||||
subagent_type: "sisyphus",
|
||||
prompt: "do team-mode work",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
const result = await resolveSubagentExecution(args, ctx, "sisyphus", "")
|
||||
|
||||
//#then — sisyphus may fail for primary-agent reasons (separate guard), but NOT the coordinator guard
|
||||
expect(result.error).not.toContain("coordinator agent")
|
||||
})
|
||||
|
||||
test("#given subagent_type=atlas #when resolveSubagentExecution is called #then atlas is NOT blocked by coordinator guard (registry: eligible)", async () => {
|
||||
//#given — atlas is verdict:'eligible' in AGENT_ELIGIBILITY_REGISTRY; it must not be rejected by the coordinator guard
|
||||
const ctx = makeCtx()
|
||||
const args = {
|
||||
subagent_type: "atlas",
|
||||
prompt: "do team-mode work",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
const result = await resolveSubagentExecution(args, ctx, "sisyphus", "")
|
||||
|
||||
//#then — atlas may fail for primary-agent reasons (separate guard), but NOT the coordinator guard
|
||||
expect(result.error).not.toContain("coordinator agent")
|
||||
})
|
||||
|
||||
test("#given subagent_type=prometheus AND allowPrimaryAgentDelegation=true #when resolveSubagentExecution is called #then prometheus is STILL rejected (registry hard-reject is authoritative)", async () => {
|
||||
//#given — prometheus is verdict:'hard-reject' in AGENT_ELIGIBILITY_REGISTRY; the coordinator guard must fire even when the team-mode resolver opts into primary-agent delegation
|
||||
const ctx = makeCtx()
|
||||
const args = {
|
||||
subagent_type: "prometheus",
|
||||
prompt: "plan something",
|
||||
load_skills: [],
|
||||
run_in_background: false,
|
||||
description: "test delegation",
|
||||
}
|
||||
|
||||
//#when
|
||||
const result = await resolveSubagentExecution(args, ctx, "sisyphus", "", { allowPrimaryAgentDelegation: true })
|
||||
|
||||
//#then
|
||||
expect(result.error).toContain("prometheus")
|
||||
expect(result.error).toContain("coordinator agent")
|
||||
expect(result.agentToUse).toBe("")
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
|
||||
@@ -165,7 +165,10 @@ describe("resolveSubagentExecution", () => {
|
||||
//#then
|
||||
expect(result.agentToUse).toBe("")
|
||||
expect(result.categoryModel).toBeUndefined()
|
||||
expect(result.error).toBe('Cannot delegate to primary agent "Prometheus - Plan Builder" via task. Select that agent directly instead.')
|
||||
// Prometheus is registry-hard-reject (AGENT_ELIGIBILITY_REGISTRY); the coordinator guard (#4027 / #4071) fires before
|
||||
// the primary-agent guard. Either rejection message is acceptable as long as prometheus is blocked from delegation.
|
||||
expect(result.error).toContain('"Prometheus - Plan Builder"')
|
||||
expect(result.error).toMatch(/Cannot delegate to (coordinator agent|primary agent)/)
|
||||
})
|
||||
|
||||
test("allows delegating to a primary agent when allowPrimaryAgentDelegation is enabled (team-mode path)", async () => {
|
||||
|
||||
Reference in New Issue
Block a user