feat(athena): expand council intent system from 4 to 8 intents

Add DIAGNOSE, CREATE, PERSPECTIVES, FREEFORM intents to Athena's council
classification system. Refine existing AUDIT (add DIAGNOSE boundary),
EVALUATE (improve criteria guidance), and EXPLAIN (remove catch-all status).

Fix bug where prepare_council_prompt defaulted to AUDIT when no intent was
specified — this caused analytical frameworks to be injected into creative
requests (e.g. poetry). Now defaults to no addendum.

Derive validIntents from Object.keys(COUNCIL_INTENT_ADDENDUMS) to prevent
drift between the addendums record and the validation array.
This commit is contained in:
ismeth
2026-02-28 12:17:16 +01:00
committed by YeonGyu-Kim
parent f5829bf2fb
commit c27b309320
3 changed files with 249 additions and 60 deletions
+180 -42
View File
@@ -1,4 +1,57 @@
export const COUNCIL_INTENT_ADDENDUMS: Record<string, string> = {
DIAGNOSE: `
## Analysis Intent: DIAGNOSE
You are conducting a **diagnosis** — your goal is to trace an observed problem back to its root cause through systematic investigation.
**Focus:**
- Start from the reported symptom — what is actually observed vs. what is expected?
- Form hypotheses about potential causes, ordered by likelihood
- For each hypothesis, gather concrete evidence that confirms or eliminates it
- Follow the causal chain backward: symptom ← immediate cause ← deeper cause ← root cause
- Distinguish the root cause from contributing factors and coincidental observations
- Propose a fix that addresses the root cause, not just the symptom
- You are NOT scanning broadly for issues — you are investigating a specific problem
**Required output fields:**
- Symptom (observed vs. expected), Hypotheses investigated (with evidence for/against each)
- Root cause (with causal chain and confidence), Contributing factors
- Recommended fix (targeting root cause, with verification approach)
**Structure your response as:**
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
## Symptom
[What is happening vs. what should be happening]
## Investigation
### Hypothesis 1: [description]
- **Likelihood**: high/medium/low
- **Evidence for**: [what supports this hypothesis]
- **Evidence against**: [what contradicts it]
- **Verdict**: confirmed / eliminated / inconclusive
### Hypothesis 2: [description]
...
## Root Cause
- **What**: [the root cause, stated in one clear sentence]
- **Causal Chain**: [symptom] ← [immediate cause] ← [root cause]
- **Confidence**: high/medium/low
- **Key Evidence**: [the specific evidence that pinpoints this as the root cause]
## Contributing Factors
- [Conditions that aren't the root cause but make the problem worse or more frequent]
## Recommended Fix
- **Target**: [what specifically to change]
- **Verification**: [how to confirm the symptom is resolved]
## Summary
[One-paragraph diagnosis with overall confidence level]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
AUDIT: `
## Analysis Intent: AUDIT
@@ -10,6 +63,7 @@ You are conducting an **audit** — your goal is to find discrete issues, risks,
- Severity determines priority: critical (blocks/breaks), high (significant risk), medium (should fix), low (nice to fix)
- For each finding, provide the specific location (reference, section, or component where it occurs)
- State your confidence: high (clear evidence), medium (likely but needs verification), low (suspicion, investigate further)
- **This is a broad sweep, not a targeted trace.** If you are starting from a specific symptom and need to find its root cause, that is DIAGNOSE — not AUDIT.
**Required output fields per finding:**
- Title, Severity (critical/high/medium/low), Location, Confidence (high/medium/low)
@@ -32,47 +86,6 @@ You are conducting an **audit** — your goal is to find discrete issues, risks,
## Summary
[Total findings by severity. Overall risk assessment with confidence levels.]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
EVALUATE: `
## Analysis Intent: EVALUATE
You are conducting an **evaluation** — your goal is to compare options against criteria and surface tradeoffs.
**Focus:**
- Identify the options available (explicit or implied by the question)
- Define evaluation criteria relevant to the context (cost, complexity, performance, maintainability, risk, etc.)
- Assess each option against each criterion with evidence, not assumptions
- Surface tradeoffs clearly — where one option wins, another likely loses
- If the question implies a recommendation is needed, provide one with conditions
**Required output fields:**
- Options identified, Criteria used, Per-option assessment, Tradeoff summary
- Conditional recommendation (if decision is required), Confidence per assessment
**Structure your response as:**
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
## Options Identified
1. [Option A] — [brief description]
2. [Option B] — [brief description]
## Criteria & Assessment
| Criterion | Option A | Option B |
|-----------|----------|----------|
| [e.g. Complexity] | [assessment] | [assessment] |
## Tradeoff Summary
- Option A excels at [X] but sacrifices [Y]
- Option B excels at [Y] but sacrifices [X]
## Recommendation
[If applicable: "Choose A if [condition]; choose B if [condition]"]
- **Confidence**: [level and reasoning]
## Risks & Unknowns
[Uncertainties that could change the recommendation]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
PLAN: `
@@ -118,6 +131,47 @@ You are conducting a **planning analysis** — your goal is to define current st
## Summary
[Overall effort assessment, key risks, confidence in feasibility]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
EVALUATE: `
## Analysis Intent: EVALUATE
You are conducting an **evaluation** — your goal is to compare options against criteria and surface tradeoffs.
**Focus:**
- Identify the options available (explicit or implied by the question)
- Define evaluation criteria relevant to the specific decision context (not generic checklists — choose criteria that actually differentiate the options)
- Assess each option against each criterion with evidence, not assumptions
- Surface tradeoffs clearly — where one option wins, another likely loses
- If the question implies a recommendation is needed, provide one with conditions
**Required output fields:**
- Options identified, Criteria used, Per-option assessment, Tradeoff summary
- Conditional recommendation (if decision is required), Confidence per assessment
**Structure your response as:**
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
## Options Identified
1. [Option A] — [brief description]
2. [Option B] — [brief description]
## Criteria & Assessment
| Criterion | Option A | Option B |
|-----------|----------|----------|
| [e.g. Complexity] | [assessment] | [assessment] |
## Tradeoff Summary
- Option A excels at [X] but sacrifices [Y]
- Option B excels at [Y] but sacrifices [X]
## Recommendation
[If applicable: "Choose A if [condition]; choose B if [condition]"]
- **Confidence**: [level and reasoning]
## Risks & Unknowns
[Uncertainties that could change the recommendation]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
EXPLAIN: `
@@ -131,7 +185,9 @@ You are conducting an **explanatory analysis** — your goal is to build underst
- Provide evidence and references for each mechanism (not just assertions)
- Map relationships, data flows, or causal chains between components
- Acknowledge unknowns, gaps in evidence, and areas of uncertainty
- Works for any domain: architecture, external systems, abstract concepts, research topics
**Use this intent for:** "how does X work", "explain Y", "what is Z", "why does this exist", "describe the architecture of..." questions.
**This is NOT a catch-all.** If the question doesn't fit this focus, it likely belongs in FREEFORM.
**Required output fields:**
- Thesis statement, Key mechanisms/components (with evidence)
@@ -163,5 +219,87 @@ You are conducting an **explanatory analysis** — your goal is to build underst
## Summary
[Synthesized explanation with overall confidence level]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
CREATE: `
## Analysis Intent: CREATE
You are here to **produce something** — not to analyze, evaluate, or plan it.
**Focus:**
- Produce the actual deliverable directly — code, prose, design, spec, whatever was asked for
- Let the deliverable type dictate its own natural structure
- Write at a professional level, as if this will be used directly
- Be thorough and complete — a finished piece beats a polished fragment
- Make creative choices decisively; note significant ones briefly at the end
**You are not an analyst right now. Stop analyzing. Start making.**
Do not produce a findings report. Do not produce a severity matrix. Do not produce an options comparison. Produce the thing that was asked for.
**Structure your response as:**
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
[Your complete deliverable. Structure it however is natural for what you're creating. There is no prescribed internal format.]
---
*Choices: [Optional — only if significant decisions aren't self-evident]*
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
PERSPECTIVES: `
## Analysis Intent: PERSPECTIVES
You are **surfacing genuine viewpoints and taking a stand** — your goal is to map the intellectual landscape, then declare where you land.
**Focus:**
- Identify 2-4 perspectives that are genuinely held — real positions, not straw men
- Argue each perspective at its strongest — as if you believed it
- Identify the crux: what assumption must be true for each position to be right?
- After presenting perspectives, declare YOUR position and defend it
- Name what you're giving up by not choosing another perspective
**Neutral summaries are failure.** If you find every perspective equally valid, you have not thought hard enough. Take a position.
**Required output fields:**
- 2-4 named perspectives (each: position, evidence, crux)
- Core tensions between perspectives
- Your declared position with reasoning and confidence
**Structure your response as:**
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
## Perspective 1: [Name/Label]
- **Position**: [what this perspective argues — be precise]
- **Strongest evidence**: [why a thoughtful person holds this view]
- **What it gets right**: [genuine strengths, honestly stated]
- **Crux**: [what assumption must be true for this to be right]
## Perspective 2: [Name/Label]
...
## Core Tensions
[Where these perspectives actually collide — incompatible assumptions or values]
## My Position
[Which view you find most defensible. Be direct. Name the cost — what you're giving up or what remains uncertain even given your stance.]
- **Confidence**: high/medium/low
- **What would change my mind**: [specific conditions or evidence]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
FREEFORM: `
## Analysis Intent: FREEFORM
Respond naturally to the question below. No analytical framework is imposed.
Use whatever structure serves the answer best. Analyze if analysis helps. Answer directly if a direct answer is better. Be conversational if the question is conversational.
You MUST still wrap your response in \`<COUNCIL_MEMBER_RESPONSE>\` tags for extraction.
\`\`\`
<COUNCIL_MEMBER_RESPONSE>
[Your response — structured however is most appropriate for the question]
</COUNCIL_MEMBER_RESPONSE>
\`\`\``,
}
+52 -3
View File
@@ -75,16 +75,65 @@ describe("createPrepareCouncilPromptTool", () => {
})
})
describe("#when called with intent DIAGNOSE", () => {
it("#then produces file containing DIAGNOSE addendum", async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-test-"))
const toolDef = createPrepareCouncilPromptTool(tmpDir)
const result = await toolDef.execute({ prompt: "Why is the API returning 500 errors?", intent: "DIAGNOSE" }, mockContext)
const filePath = extractFilePath(result)
const content = await readFile(filePath, "utf-8")
expect(content).toContain("## Analysis Intent: DIAGNOSE")
})
})
describe("#when called with intent CREATE", () => {
it("#then produces file containing CREATE addendum", async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-test-"))
const toolDef = createPrepareCouncilPromptTool(tmpDir)
const result = await toolDef.execute({ prompt: "Write a poem about TypeScript", intent: "CREATE" }, mockContext)
const filePath = extractFilePath(result)
const content = await readFile(filePath, "utf-8")
expect(content).toContain("## Analysis Intent: CREATE")
})
})
describe("#when called with intent PERSPECTIVES", () => {
it("#then produces file containing PERSPECTIVES addendum", async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-test-"))
const toolDef = createPrepareCouncilPromptTool(tmpDir)
const result = await toolDef.execute({ prompt: "What do you think about microservices?", intent: "PERSPECTIVES" }, mockContext)
const filePath = extractFilePath(result)
const content = await readFile(filePath, "utf-8")
expect(content).toContain("## Analysis Intent: PERSPECTIVES")
})
})
describe("#when called with intent FREEFORM", () => {
it("#then produces file containing FREEFORM addendum", async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-test-"))
const toolDef = createPrepareCouncilPromptTool(tmpDir)
const result = await toolDef.execute({ prompt: "Tell me something interesting", intent: "FREEFORM" }, mockContext)
const filePath = extractFilePath(result)
const content = await readFile(filePath, "utf-8")
expect(content).toContain("## Analysis Intent: FREEFORM")
})
})
describe("#when called without intent", () => {
it("#then defaults to AUDIT addendum", async () => {
it("#then produces file without any intent addendum", async () => {
tmpDir = await mkdtemp(join(tmpdir(), "council-test-"))
const toolDef = createPrepareCouncilPromptTool(tmpDir)
const result = await toolDef.execute({ prompt: "Review this module" }, mockContext)
expect(result).toContain("intent: AUDIT")
expect(result).toContain("intent: none")
const filePath = extractFilePath(result)
const content = await readFile(filePath, "utf-8")
expect(content).toContain("## Analysis Intent: AUDIT")
expect(content).not.toContain("## Analysis Intent:")
expect(content).toContain("## Analysis Question")
})
})
+17 -15
View File
@@ -45,10 +45,16 @@ The "mode" parameter controls whether council members can delegate exploration t
- "delegation": Members can delegate to explore/librarian agents. Faster, lighter context.
The "intent" parameter controls the analysis framework injected into the prompt:
- "AUDIT" (default): Find issues, risks, violations with severity ratings.
- "EVALUATE": Compare options against criteria, surface tradeoffs.
- "DIAGNOSE": Trace a specific problem to its root cause through systematic investigation.
- "AUDIT": Find issues, risks, violations with severity ratings (broad sweep).
- "PLAN": Define current state, target state, phased path.
- "EVALUATE": Compare options against criteria, surface tradeoffs.
- "EXPLAIN": Build understanding of mechanisms and relationships.
- "CREATE": Produce a deliverable directly (code, prose, design, spec).
- "PERSPECTIVES": Surface genuine viewpoints, argue each at its strongest, take a position.
- "FREEFORM": No analytical framework imposed — respond naturally.
If no intent is specified, no analysis framework is injected.
Returns the file path to reference in subsequent task() calls.`
@@ -61,7 +67,7 @@ Returns the file path to reference in subsequent task() calls.`
args: {
prompt: tool.schema.string().describe("The full analysis prompt/question for council members"),
mode: tool.schema.string().optional().describe('Analysis mode: "solo" (default) or "delegation"'),
intent: tool.schema.string().optional().describe('Question intent: "AUDIT", "EVALUATE", "PLAN", "EXPLAIN"'),
intent: tool.schema.string().optional().describe('Question intent: "DIAGNOSE", "AUDIT", "PLAN", "EVALUATE", "EXPLAIN", "CREATE", "PERSPECTIVES", "FREEFORM"'),
},
async execute(args: { prompt: string; mode?: string; intent?: string }) {
if (!args.prompt?.trim()) {
@@ -72,12 +78,12 @@ Returns the file path to reference in subsequent task() calls.`
return `Invalid mode: "${args.mode}". Valid modes: "solo", "delegation".`
}
const validIntents = ["AUDIT", "EVALUATE", "PLAN", "EXPLAIN"]
const validIntents = Object.keys(COUNCIL_INTENT_ADDENDUMS)
if (args.intent !== undefined && !validIntents.includes(args.intent.toUpperCase())) {
return `Invalid intent: "${args.intent}". Valid intents: "AUDIT", "EVALUATE", "PLAN", "EXPLAIN".`
return `Invalid intent: "${args.intent}". Valid intents: ${validIntents.map((i) => `"${i}"`).join(", ")}.`
}
const resolvedIntent = args.intent?.toUpperCase() ?? "AUDIT"
const resolvedIntent = args.intent?.toUpperCase()
const mode = args.mode === "delegation" ? "delegation" : "solo"
@@ -89,14 +95,10 @@ Returns the file path to reference in subsequent task() calls.`
const filePath = join(tmpDir, filename)
const modeAddendum = mode === "delegation" ? COUNCIL_DELEGATION_ADDENDUM : COUNCIL_SOLO_ADDENDUM
const intentAddendum = COUNCIL_INTENT_ADDENDUMS[resolvedIntent] ?? COUNCIL_INTENT_ADDENDUMS["AUDIT"]
const content = `${modeAddendum}
${intentAddendum}
## Analysis Question
${args.prompt}`
const intentAddendum = resolvedIntent ? (COUNCIL_INTENT_ADDENDUMS[resolvedIntent] ?? "") : ""
const content = intentAddendum
? `${modeAddendum}\n\n${intentAddendum}\n\n## Analysis Question\n\n${args.prompt}`
: `${modeAddendum}\n\n## Analysis Question\n\n${args.prompt}`
await writeFile(filePath, content, "utf-8")
@@ -108,7 +110,7 @@ ${args.prompt}`
log("[prepare-council-prompt] Saved prompt", { filePath, length: args.prompt.length, mode })
return `Council prompt saved to: ${filePath} (mode: ${mode}, intent: ${resolvedIntent})
return `Council prompt saved to: ${filePath} (mode: ${mode}, intent: ${resolvedIntent ?? "none"})
Use this path in each council member's task() call:
- prompt: "Read ${filePath} for your instructions."