Files
oh-my-opencode/src/tools/council-archive/create-council-finalize.test.ts
T

524 lines
22 KiB
TypeScript

/// <reference types="bun-types" />
import { describe, expect, it, beforeEach } from "bun:test"
import { mkdtemp, mkdir, writeFile, readFile } from "node:fs/promises"
import { join } from "node:path"
import { tmpdir } from "node:os"
import { createCouncilFinalize } from "./create-council-finalize"
import type { CouncilFinalizeResult } from "./types"
function mockTaskOutput(agent: string, responseBody: string, complete = true): string {
const closing = complete ? "\n</COUNCIL_MEMBER_RESPONSE>" : ""
return [
"---",
`task_id: bg_test`,
`agent: ${agent}`,
`session_id: ses_test`,
`parent_session_id: ses_parent`,
`status: completed`,
`completed_at: 2026-02-27T14:00:00.000Z`,
"---",
"",
"[assistant] 14:00:00",
`<COUNCIL_MEMBER_RESPONSE>`,
responseBody,
closing,
].join("\n")
}
function extractJson(output: string): string {
const marker = "<athena_runtime_guidance>"
const idx = output.indexOf(marker)
if (idx === -1) return output
const beforeMarker = output.substring(0, idx)
const lastNewlines = beforeMarker.lastIndexOf("\n\n")
return lastNewlines === -1 ? beforeMarker.trim() : output.substring(0, lastNewlines).trim()
}
const mockCtx = {
sessionID: "test-session",
messageID: "test-message",
agent: "test-agent",
abort: new AbortController().signal,
}
describe("createCouncilFinalize", () => {
let tmpDir: string
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-finalize-"))
await mkdir(join(tmpDir, ".sisyphus", "task-outputs"), { recursive: true })
})
describe("#given 3 members with valid output files", () => {
it("#then creates full archive with all has_response true", async () => {
const agents = [
{ id: "bg_001", agent: "Council: Claude Opus", response: "Opus analysis: This is a detailed analysis from Opus model covering the full scope of the council question." },
{ id: "bg_002", agent: "Council: GPT-5", response: "GPT analysis: This is a detailed analysis from GPT model covering the full scope of the council question." },
{ id: "bg_003", agent: "Council: Gemini", response: "Gemini analysis: This is a detailed analysis from Gemini model covering the full scope of the council question." },
]
for (const a of agents) {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", `${a.id}.md`),
mockTaskOutput(a.agent, a.response),
"utf-8",
)
}
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: agents.map((a) => a.id), name: "test", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toMatch(/\.sisyphus\/athena\/council-test-[a-f0-9]{16}$/)
expect(result.meta_file).toMatch(/\.sisyphus\/athena\/council-test-[a-f0-9]{16}\/meta\.yaml$/)
expect(result.members).toHaveLength(3)
for (let i = 0; i < agents.length; i++) {
const member = result.members[i]
expect(member.task_id).toBe(agents[i].id)
expect(member.has_response).toBe(true)
expect(member.response_complete).toBe(true)
expect(member.error).toBeUndefined()
expect(member.archive_file).toBeDefined()
}
const opusArchive = await readFile(join(tmpDir, result.members[0].archive_file!), "utf-8")
expect(opusArchive).toBe("Opus analysis: This is a detailed analysis from Opus model covering the full scope of the council question.")
const metaContent = await readFile(join(tmpDir, result.meta_file), "utf-8")
expect(metaContent).toContain("archive_name: council-test-")
expect(metaContent).toContain("created_at:")
expect(metaContent).toContain('member: "Council: Claude Opus"')
expect(metaContent).toContain("member_slug: council-claude-opus")
expect(metaContent).toContain("has_response: true")
expect(metaContent).toContain("response_complete: true")
})
})
describe("#given 1 of 3 output files missing", () => {
it("#then returns partial success with error for missing member", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_001.md"),
mockTaskOutput("Council: Claude Opus", "Opus findings: This is a detailed analysis from Opus model covering the full scope of the council question."),
"utf-8",
)
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_003.md"),
mockTaskOutput("Council: Gemini", "Gemini findings: This is a detailed analysis from Gemini model covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_001", "bg_002", "bg_003"], name: "partial", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.members).toHaveLength(3)
expect(result.members[0].has_response).toBe(true)
expect(result.members[0].archive_file).toBeDefined()
expect(result.members[1].has_response).toBe(false)
expect(result.members[1].error).toBe("Task output file not found")
expect(result.members[1].member).toBe("unknown")
expect(result.members[2].has_response).toBe(true)
expect(result.members[2].archive_file).toBeDefined()
})
})
describe("#given large response exceeding 8000 chars", () => {
it("#then keeps full content in archive readable via readFile", async () => {
const largeResponse = "x".repeat(9000)
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_large.md"),
mockTaskOutput("Council: Claude Opus", largeResponse),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_large"], name: "large", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
const member = result.members[0]
expect(member.has_response).toBe(true)
expect(member.archive_file).toBeDefined()
const archiveContent = await readFile(join(tmpDir, member.archive_file!), "utf-8")
expect(archiveContent).toHaveLength(9000)
expect(archiveContent).toBe(largeResponse)
})
})
describe("#given empty response between tags", () => {
it("#then returns has_response false with empty string result", async () => {
const emptyOutput = [
"---",
"task_id: bg_empty",
"agent: Council: Empty Agent",
"session_id: ses_test",
"parent_session_id: ses_parent",
"status: completed",
"completed_at: 2026-02-27T14:00:00.000Z",
"---",
"",
"[assistant] 14:00:00",
"<COUNCIL_MEMBER_RESPONSE></COUNCIL_MEMBER_RESPONSE>",
].join("\n")
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_empty.md"),
emptyOutput,
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_empty"], name: "empty", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
const member = result.members[0]
expect(member.has_response).toBe(false)
})
})
describe("#given two members that slugify to same member slug", () => {
it("#then each member gets a unique archive_file keyed by task id", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_alpha.md"),
mockTaskOutput("Agent A+B", "First response: This is a detailed analysis from Agent A+B covering the full scope of the council question."),
"utf-8",
)
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_beta.md"),
mockTaskOutput("Agent A B", "Second response: This is a detailed analysis from Agent A B covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_alpha", "bg_beta"], name: "collision", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
const firstArchive = result.members[0].archive_file
const secondArchive = result.members[1].archive_file
expect(firstArchive).toBeDefined()
expect(secondArchive).toBeDefined()
expect(firstArchive).not.toBe(secondArchive)
const firstContent = await readFile(join(tmpDir, firstArchive!), "utf-8")
const secondContent = await readFile(join(tmpDir, secondArchive!), "utf-8")
expect(firstContent).toContain("First response")
expect(secondContent).toContain("Second response")
})
})
describe("#given intent-based runtime guidance injection", () => {
it("#then registers critical custom context for Athena runtime guidance", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_intent.md"),
mockTaskOutput("Council: GPT-5", "Plan proposal: This is a detailed analysis from GPT model covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const result = await toolDef.execute(
{ task_ids: ["bg_intent"], name: "intent", intent: "PLAN" },
mockCtx,
)
expect(() => JSON.parse(extractJson(result))).not.toThrow()
expect(result).toContain("<athena_runtime_guidance>")
expect(result).toContain("intent: PLAN")
expect(result).toContain("Plan full scope (Prometheus)")
expect(result).toContain("Plan selected phase (Prometheus)")
expect(result).toContain(".sisyphus/athena/notes/")
expect(result).not.toContain("Hand off to Atlas to save the plan as .md")
})
it("#then emits diagnose action options for hephaestus and sisyphus", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_diagnose.md"),
mockTaskOutput("Council: Claude", "Root cause found: This is a detailed analysis from Claude model covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const result = await toolDef.execute(
{ task_ids: ["bg_diagnose"], name: "diagnose", intent: "DIAGNOSE" },
mockCtx,
)
expect(() => JSON.parse(extractJson(result))).not.toThrow()
expect(result).toContain("<athena_runtime_guidance>")
expect(result).toContain("Implement (Hephaestus)")
expect(result).toContain("Implement (Sisyphus)")
expect(result).toContain("Implement (Sisyphus ultrawork)")
expect(result).toContain("switch_agent(agent=\"hephaestus\")")
expect(result).toContain("switch_agent(agent=\"sisyphus\")")
expect(result).toContain("prefix the handoff context with \"ultrawork \"")
expect(result).not.toContain("Fix now (Atlas)")
expect(result).not.toContain("Create plan (Prometheus)")
})
it("#then emits audit processing mode and batching guidance", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_audit.md"),
mockTaskOutput("Council: Claude", "Audit findings: This is a detailed analysis from Claude model covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const result = await toolDef.execute(
{ task_ids: ["bg_audit"], name: "audit", intent: "AUDIT" },
mockCtx,
)
expect(() => JSON.parse(extractJson(result))).not.toThrow()
expect(result).toContain("<athena_runtime_guidance>")
expect(result).toContain("How would you like to process the findings?")
expect(result).toContain("One by one")
expect(result).toContain("By severity/urgency")
expect(result).toContain("By quorum")
expect(result).toContain("Default batch size: 3 findings per batch")
expect(result).toContain("Hard cap: 5 findings")
expect(result).toContain("Example Question tool call (batch of 3 findings)")
expect(result).toContain("Finding #10: choose how to proceed.")
expect(result).toContain("#10 Action")
expect(result).toContain("Stop review")
expect(result).toContain("#10:A, #11:skip")
expect(result).toContain("Which findings should we act on by severity?")
expect(result).toContain("All Critical (N)")
expect(result).toContain("All High (N)")
expect(result).toContain("All Medium (N)")
expect(result).toContain("All Low (N)")
expect(result).toContain("Which findings should we act on? You can also type specific finding numbers")
expect(result).toContain("All Unanimous (N)")
expect(result).toContain("All Majority (N)")
expect(result).toContain("All Minority (N)")
expect(result).toContain("All Solo (N)")
expect(result).toContain("Fix now (Atlas)")
expect(result).toContain("Create plan (Prometheus)")
})
it("#then emits informational write-to-document path without atlas delegation", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_eval.md"),
mockTaskOutput("Council: Claude", "Option comparison: This is a detailed analysis from Claude model covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const result = await toolDef.execute(
{ task_ids: ["bg_eval"], name: "eval", intent: "EVALUATE" },
mockCtx,
)
expect(() => JSON.parse(extractJson(result))).not.toThrow()
expect(result).toContain("<athena_runtime_guidance>")
expect(result).toContain("What should we do with this evaluation?")
expect(result).toContain("Adopt option -> create plan (Prometheus)")
expect(result).toContain("Adopt option -> implement now")
expect(result).toContain(".sisyphus/athena/notes/")
expect(result).not.toContain("Write to document (Atlas)")
})
it("#then rejects invalid intent values", async () => {
const toolDef = createCouncilFinalize(tmpDir)
const result = await toolDef.execute(
{ task_ids: ["bg_none"], name: "invalid-intent", intent: "NOT_A_REAL_INTENT" },
mockCtx,
)
expect(result).toContain("Invalid intent")
expect(result).toContain("NOT_A_REAL_INTENT")
})
})
describe("#given path traversal attempts", () => {
it("#then rejects task IDs with path traversal characters", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_valid.md"),
mockTaskOutput("Agent", "Valid response: This is a detailed analysis from Agent covering the full scope of the council question."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["../../etc/passwd", "bg_valid", "foo/bar"], name: "traversal", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.members).toHaveLength(3)
expect(result.members[0].has_response).toBe(false)
expect(result.members[0].error).toContain("Invalid task ID")
expect(result.members[1].has_response).toBe(true)
expect(result.members[2].has_response).toBe(false)
expect(result.members[2].error).toContain("Invalid task ID")
})
it("#then sanitizes name with path traversal characters", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_safe.md"),
mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_safe"], name: "../../etc", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toMatch(/^\.sisyphus\/athena\/council-/)
expect(result.archive_dir).not.toContain("..")
})
it("#then sanitizes name with absolute path", async () => {
await writeFile(join(tmpDir, ".sisyphus", "task-outputs", "bg_abs.md"), mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."), "utf-8")
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_abs"], name: "/absolute/path", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toMatch(/^\.sisyphus\/athena\/council-/)
expect(result.archive_dir).not.toContain("/absolute/path")
})
it("#then sanitizes name with dot-dot-slash in the middle", async () => {
await writeFile(join(tmpDir, ".sisyphus", "task-outputs", "bg_mid.md"), mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."), "utf-8")
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_mid"], name: "foo/../bar", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toMatch(/^\.sisyphus\/athena\/council-/)
expect(result.archive_dir).not.toContain("..")
})
it("#then accepts valid task IDs", async () => {
await writeFile(join(tmpDir, ".sisyphus", "task-outputs", "valid-id_123.md"), mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."), "utf-8")
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["valid-id_123"], name: "valid", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.members).toHaveLength(1)
expect(result.members[0].has_response).toBe(true)
expect(result.members[0].error).toBeUndefined()
})
it("#then rejects prompt_file with absolute path outside workspace", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_prompt.md"),
mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_prompt"], name: "prompt-test", prompt_file: "/etc/passwd", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toBeDefined()
const metaContent = await readFile(join(tmpDir, result.meta_file), "utf-8")
expect(metaContent).not.toContain("prompt_file:")
})
it("#then rejects prompt_file with relative traversal", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_rel.md"),
mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_rel"], name: "rel-test", prompt_file: "../../etc/passwd", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toBeDefined()
const metaContent = await readFile(join(tmpDir, result.meta_file), "utf-8")
expect(metaContent).not.toContain("prompt_file:")
})
it("#then accepts valid prompt_file under .sisyphus/tmp/", async () => {
await mkdir(join(tmpDir, ".sisyphus", "tmp"), { recursive: true })
await writeFile(join(tmpDir, ".sisyphus", "tmp", "athena-council-test.md"), "Test prompt", "utf-8")
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_ok.md"),
mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_ok"], name: "valid-prompt", prompt_file: ".sisyphus/tmp/athena-council-test.md", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
const metaContent = await readFile(join(tmpDir, result.meta_file), "utf-8")
expect(metaContent).toContain("prompt_file:")
})
it("#then rejects task IDs with backslash characters", async () => {
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg\\evil"], name: "backslash", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.members).toHaveLength(1)
expect(result.members[0].has_response).toBe(false)
expect(result.members[0].error).toContain("Invalid task ID")
})
it("#then handles name that slugifies to empty string", async () => {
await writeFile(
join(tmpDir, ".sisyphus", "task-outputs", "bg_empty_name.md"),
mockTaskOutput("Agent", "Response: This is a detailed analysis from Agent covering the full scope of the council question here."),
"utf-8",
)
const toolDef = createCouncilFinalize(tmpDir)
const resultStr = await toolDef.execute(
{ task_ids: ["bg_empty_name"], name: "///...", intent: "FREEFORM" },
mockCtx,
)
const result: CouncilFinalizeResult = JSON.parse(extractJson(resultStr))
expect(result.archive_dir).toMatch(/^\.sisyphus\/athena\/council-unnamed-[a-f0-9]{16}$/)
})
})
})