2026-03-01 00:18:39 +01:00
/// <reference types="bun-types" />
2026-02-27 15:15:26 +01:00
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" )
}
2026-03-01 18:36:27 +01:00
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 ( )
}
2026-02-27 15:15:26 +01:00
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 = [
2026-03-01 16:48:12 +01:00
{ 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." } ,
2026-02-27 15:15:26 +01:00
]
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 (
2026-03-01 16:48:12 +01:00
{ task_ids : agents.map ( ( a ) = > a . id ) , name : "test" , intent : "FREEFORM" } ,
2026-02-27 15:15:26 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-02-27 15:15:26 +01:00
2026-03-02 00:34:53 +01:00
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$/ )
2026-02-27 15:15:26 +01:00
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" )
2026-03-01 16:48:12 +01:00
expect ( opusArchive ) . toBe ( "Opus analysis: This is a detailed analysis from Opus model covering the full scope of the council question." )
2026-02-27 15:15:26 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: Claude Opus" , "Opus findings: This is a detailed analysis from Opus model covering the full scope of the council question." ) ,
2026-02-27 15:15:26 +01:00
"utf-8" ,
)
await writeFile (
join ( tmpDir , ".sisyphus" , "task-outputs" , "bg_003.md" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: Gemini" , "Gemini findings: This is a detailed analysis from Gemini model covering the full scope of the council question." ) ,
2026-02-27 15:15:26 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_001" , "bg_002" , "bg_003" ] , name : "partial" , intent : "FREEFORM" } ,
2026-02-27 15:15:26 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-02-27 15:15:26 +01:00
expect ( result . members ) . toHaveLength ( 3 )
expect ( result . members [ 0 ] . has_response ) . toBe ( true )
2026-02-28 16:19:19 +01:00
expect ( result . members [ 0 ] . archive_file ) . toBeDefined ( )
2026-02-27 15:15:26 +01:00
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 )
2026-02-28 16:19:19 +01:00
expect ( result . members [ 2 ] . archive_file ) . toBeDefined ( )
2026-02-27 15:15:26 +01:00
} )
} )
describe ( "#given large response exceeding 8000 chars" , ( ) = > {
2026-03-01 14:49:30 +01:00
it ( "#then keeps full content in archive readable via readFile" , async ( ) = > {
2026-02-27 15:15:26 +01:00
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 (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_large" ] , name : "large" , intent : "FREEFORM" } ,
2026-02-27 15:15:26 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-02-27 15:15:26 +01:00
const member = result . members [ 0 ]
expect ( member . has_response ) . toBe ( true )
2026-02-28 16:19:19 +01:00
expect ( member . archive_file ) . toBeDefined ( )
2026-03-01 14:49:30 +01:00
const archiveContent = await readFile ( join ( tmpDir , member . archive_file ! ) , "utf-8" )
expect ( archiveContent ) . toHaveLength ( 9000 )
expect ( archiveContent ) . toBe ( largeResponse )
2026-02-27 15:15:26 +01:00
} )
} )
describe ( "#given empty response between tags" , ( ) = > {
2026-03-01 16:48:12 +01:00
it ( "#then returns has_response false with empty string result" , async ( ) = > {
2026-02-27 15:15:26 +01:00
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 (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_empty" ] , name : "empty" , intent : "FREEFORM" } ,
2026-02-27 15:15:26 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-02-27 15:15:26 +01:00
const member = result . members [ 0 ]
2026-03-01 16:48:12 +01:00
expect ( member . has_response ) . toBe ( false )
2026-02-28 16:19:19 +01:00
} )
} )
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent A+B" , "First response: This is a detailed analysis from Agent A+B covering the full scope of the council question." ) ,
2026-02-28 16:19:19 +01:00
"utf-8" ,
)
await writeFile (
join ( tmpDir , ".sisyphus" , "task-outputs" , "bg_beta.md" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent A B" , "Second response: This is a detailed analysis from Agent A B covering the full scope of the council question." ) ,
2026-02-28 16:19:19 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_alpha" , "bg_beta" ] , name : "collision" , intent : "FREEFORM" } ,
2026-02-28 16:19:19 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-02-28 16:19:19 +01:00
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" )
2026-02-27 15:15:26 +01:00
} )
} )
2026-03-01 00:18:39 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: GPT-5" , "Plan proposal: This is a detailed analysis from GPT model covering the full scope of the council question." ) ,
2026-03-01 00:18:39 +01:00
"utf-8" ,
)
2026-03-01 18:36:27 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
2026-03-01 00:18:39 +01:00
const result = await toolDef . execute (
{ task_ids : [ "bg_intent" ] , name : "intent" , intent : "PLAN" } ,
mockCtx ,
)
2026-03-01 18:36:27 +01:00
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" )
2026-03-01 00:18:39 +01:00
} )
it ( "#then emits diagnose action options for hephaestus and sisyphus" , async ( ) = > {
await writeFile (
join ( tmpDir , ".sisyphus" , "task-outputs" , "bg_diagnose.md" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: Claude" , "Root cause found: This is a detailed analysis from Claude model covering the full scope of the council question." ) ,
2026-03-01 00:18:39 +01:00
"utf-8" ,
)
2026-03-01 18:36:27 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
2026-03-01 00:18:39 +01:00
const result = await toolDef . execute (
{ task_ids : [ "bg_diagnose" ] , name : "diagnose" , intent : "DIAGNOSE" } ,
mockCtx ,
)
2026-03-01 18:36:27 +01:00
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)" )
2026-03-01 00:18:39 +01:00
} )
it ( "#then emits audit processing mode and batching guidance" , async ( ) = > {
await writeFile (
join ( tmpDir , ".sisyphus" , "task-outputs" , "bg_audit.md" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: Claude" , "Audit findings: This is a detailed analysis from Claude model covering the full scope of the council question." ) ,
2026-03-01 00:18:39 +01:00
"utf-8" ,
)
2026-03-01 18:36:27 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
2026-03-01 00:18:39 +01:00
const result = await toolDef . execute (
{ task_ids : [ "bg_audit" ] , name : "audit" , intent : "AUDIT" } ,
mockCtx ,
)
2026-03-01 18:36:27 +01:00
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)" )
2026-03-01 00:18:39 +01:00
} )
it ( "#then emits informational write-to-document path without atlas delegation" , async ( ) = > {
await writeFile (
join ( tmpDir , ".sisyphus" , "task-outputs" , "bg_eval.md" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Council: Claude" , "Option comparison: This is a detailed analysis from Claude model covering the full scope of the council question." ) ,
2026-03-01 00:18:39 +01:00
"utf-8" ,
)
2026-03-01 18:36:27 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
2026-03-01 00:18:39 +01:00
const result = await toolDef . execute (
{ task_ids : [ "bg_eval" ] , name : "eval" , intent : "EVALUATE" } ,
mockCtx ,
)
2026-03-01 18:36:27 +01:00
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)" )
2026-03-01 00:18:39 +01:00
} )
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" )
} )
} )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Valid response: This is a detailed analysis from Agent covering the full scope of the council question." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "../../etc/passwd" , "bg_valid" , "foo/bar" ] , name : "traversal" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Response: This is a detailed analysis from Agent covering the full scope of the council question here." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_safe" ] , name : "../../etc" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
expect ( result . archive_dir ) . toMatch ( /^\.sisyphus\/athena\/council-/ )
expect ( result . archive_dir ) . not . toContain ( ".." )
} )
it ( "#then sanitizes name with absolute path" , async ( ) = > {
2026-03-01 16:48:12 +01:00
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" )
2026-03-01 15:51:15 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_abs" ] , name : "/absolute/path" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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 ( ) = > {
2026-03-01 16:48:12 +01:00
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" )
2026-03-01 15:51:15 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_mid" ] , name : "foo/../bar" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
expect ( result . archive_dir ) . toMatch ( /^\.sisyphus\/athena\/council-/ )
expect ( result . archive_dir ) . not . toContain ( ".." )
} )
it ( "#then accepts valid task IDs" , async ( ) = > {
2026-03-01 16:48:12 +01:00
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" )
2026-03-01 15:51:15 +01:00
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "valid-id_123" ] , name : "valid" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Response: This is a detailed analysis from Agent covering the full scope of the council question here." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_prompt" ] , name : "prompt-test" , prompt_file : "/etc/passwd" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Response: This is a detailed analysis from Agent covering the full scope of the council question here." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_rel" ] , name : "rel-test" , prompt_file : "../../etc/passwd" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Response: This is a detailed analysis from Agent covering the full scope of the council question here." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_ok" ] , name : "valid-prompt" , prompt_file : ".sisyphus/tmp/athena-council-test.md" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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 (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg\\evil" ] , name : "backslash" , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
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" ) ,
2026-03-01 16:48:12 +01:00
mockTaskOutput ( "Agent" , "Response: This is a detailed analysis from Agent covering the full scope of the council question here." ) ,
2026-03-01 15:51:15 +01:00
"utf-8" ,
)
const toolDef = createCouncilFinalize ( tmpDir )
const resultStr = await toolDef . execute (
2026-03-01 16:48:12 +01:00
{ task_ids : [ "bg_empty_name" ] , name : "///..." , intent : "FREEFORM" } ,
2026-03-01 15:51:15 +01:00
mockCtx ,
)
2026-03-01 18:36:27 +01:00
const result : CouncilFinalizeResult = JSON . parse ( extractJson ( resultStr ) )
2026-03-01 15:51:15 +01:00
2026-03-02 00:34:53 +01:00
expect ( result . archive_dir ) . toMatch ( /^\.sisyphus\/athena\/council-unnamed-[a-f0-9]{16}$/ )
2026-03-01 15:51:15 +01:00
} )
} )
2026-02-27 15:15:26 +01:00
} )