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:
YeonGyu-Kim
2026-05-01 17:57:49 +09:00
parent ea5e3e034e
commit ca8732f8fe
2 changed files with 83 additions and 0 deletions
@@ -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 {
return {
version: 1,
@@ -146,6 +157,48 @@ describe("team-registry validator", () => {
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", () => {
// given
const member: Member = {
@@ -3,6 +3,12 @@ import { AGENT_ELIGIBILITY_REGISTRY } from "../types"
import type { Member, TeamSpec } from "../types"
const MAX_TEAM_MEMBERS = 8
const HYPERPLAN_REQUIRED_CATEGORIES = [
"unspecified-low",
"unspecified-high",
"ultrabrain",
"artistry",
] as const
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."
@@ -56,6 +62,30 @@ export function validateSpec(spec: TeamSpec): void {
"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 {