fix(team-mode): reject incomplete hyperplan teams
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -26,6 +26,17 @@ function createCategoryMember(name: string): Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createHyperplanMember(name: string, category: string): Member {
|
||||||
|
return {
|
||||||
|
kind: "category",
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
prompt: `perform the ${name} adversarial role`,
|
||||||
|
backendType: "in-process",
|
||||||
|
isActive: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createBaseTeamSpec(): TeamSpec {
|
function createBaseTeamSpec(): TeamSpec {
|
||||||
return {
|
return {
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -146,6 +157,48 @@ describe("team-registry validator", () => {
|
|||||||
expect(act).toThrow("Team 'validator-team' exceeds max 8 members.")
|
expect(act).toThrow("Team 'validator-team' exceeds max 8 members.")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("rejects hyperplan teams that omit required adversarial categories", () => {
|
||||||
|
// given
|
||||||
|
const teamSpec: TeamSpec = {
|
||||||
|
version: 1,
|
||||||
|
name: "hyperplan",
|
||||||
|
createdAt: 1,
|
||||||
|
leadAgentId: "architect",
|
||||||
|
members: [
|
||||||
|
createHyperplanMember("researcher", "deep"),
|
||||||
|
createHyperplanMember("architect", "ultrabrain"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const act = () => validateSpec(teamSpec)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(act).toThrow("Hyperplan team must include category 'unspecified-low'.")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("accepts hyperplan teams with required adversarial categories and optional deep", () => {
|
||||||
|
// given
|
||||||
|
const teamSpec: TeamSpec = {
|
||||||
|
version: 1,
|
||||||
|
name: "hyperplan",
|
||||||
|
createdAt: 1,
|
||||||
|
leadAgentId: "architect",
|
||||||
|
members: [
|
||||||
|
createHyperplanMember("skeptic", "unspecified-low"),
|
||||||
|
createHyperplanMember("validator", "unspecified-high"),
|
||||||
|
createHyperplanMember("architect", "ultrabrain"),
|
||||||
|
createHyperplanMember("creative", "artistry"),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const act = () => validateSpec(teamSpec)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(act).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
test("rejects category prompts that collapse to empty text", () => {
|
test("rejects category prompts that collapse to empty text", () => {
|
||||||
// given
|
// given
|
||||||
const member: Member = {
|
const member: Member = {
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import { AGENT_ELIGIBILITY_REGISTRY } from "../types"
|
|||||||
import type { Member, TeamSpec } from "../types"
|
import type { Member, TeamSpec } from "../types"
|
||||||
|
|
||||||
const MAX_TEAM_MEMBERS = 8
|
const MAX_TEAM_MEMBERS = 8
|
||||||
|
const HYPERPLAN_REQUIRED_CATEGORIES = [
|
||||||
|
"unspecified-low",
|
||||||
|
"unspecified-high",
|
||||||
|
"ultrabrain",
|
||||||
|
"artistry",
|
||||||
|
] as const
|
||||||
const UNKNOWN_SUBAGENT_MESSAGE =
|
const UNKNOWN_SUBAGENT_MESSAGE =
|
||||||
"Unknown subagent_type '<name>'. Available ELIGIBLE agents: sisyphus, atlas, sisyphus-junior, hephaestus (if D-36 applied). Use delegate-task for read-only agents like oracle, librarian, explore, metis, momus, multimodal-looker."
|
"Unknown subagent_type '<name>'. Available ELIGIBLE agents: sisyphus, atlas, sisyphus-junior, hephaestus (if D-36 applied). Use delegate-task for read-only agents like oracle, librarian, explore, metis, momus, multimodal-looker."
|
||||||
|
|
||||||
@@ -56,6 +62,30 @@ export function validateSpec(spec: TeamSpec): void {
|
|||||||
"leadAgentId",
|
"leadAgentId",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateHyperplanComposition(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateHyperplanComposition(spec: TeamSpec): void {
|
||||||
|
if (spec.name !== "hyperplan") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories = new Set(
|
||||||
|
spec.members
|
||||||
|
.filter((member) => member.kind === "category")
|
||||||
|
.map((member) => member.category),
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const category of HYPERPLAN_REQUIRED_CATEGORIES) {
|
||||||
|
if (!categories.has(category)) {
|
||||||
|
throw new TeamSpecValidationError(
|
||||||
|
`Hyperplan team must include category '${category}'.`,
|
||||||
|
"HYPERPLAN_REQUIRED_CATEGORY_MISSING",
|
||||||
|
"members",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function validateMemberEligibility(member: Member): void {
|
export function validateMemberEligibility(member: Member): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user