
Claude Council
- 230 installs
- 11 repo stars
- Updated April 21, 2026
- torpedod/claude-council
Spawns 5 parallel Agent advisors, peer review, debate round, and writes council-report HTML plus council-transcript markdown
About
claude-council from torpedod/claude-council implements a structured LLM Council inside Claude Code. After triage rejects low-stakes or factual queries, it frames the decision with workspace context and optional bias audit, then dispatches five parallel advisors (Red Team, First Principles, Expansionist, Outsider, Executor) in Quick (3 advisors), Standard, or Deep modes. Advisors undergo anonymized peer review, optional forced debate when consensus is too strong, and dual-chairman synthesis with a dissent ledger.
- Five advisor personas: Red Team, First Principles, Expansionist, Outsider, Executor
- Quick, Standard, Deep modes with 4 to 16+ parallel Agent calls
- Anonymized peer review plus optional Prosecutor/Defender debate round
- Dual-chairman synthesis with dissent ledger preservation
- Persistent council-log.jsonl journal with outcome and meta-analysis scripts
Claude Council by the numbers
- 230 all-time installs (skills.sh)
- +23 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,670 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/torpedod/claude-council --skill claude-councilAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 230 |
|---|---|
| repo stars | ★ 11 |
| Last updated | April 21, 2026 |
| Repository | torpedod/claude-council ↗ |
What it does
Spawns 5 parallel Agent advisors, peer review, debate round, and writes council-report HTML plus council-transcript markdown
Files
LLM Council
Install path assumption: This skill assumes installation at~/.claude/skills/claude-council/. If installed elsewhere (e.g.,~/.claude/plugins/<name>/skills/claude-council/), adjust script paths in Steps 0, 2, and 10 accordingly.
Run any high-stakes decision through five structured thinking lenses, a peer-review round, a forced debate when consensus is too clean, and a dual-chairman synthesis that preserves dissent. Every run logs to a journal; outcomes feed a self-improvement loop that proposes persona refinements over time.
Not for: factual lookups, debugging, single-domain technical questions, quick yes/no decisions, emotional support. If none of the options feel genuinely hard, just answer directly.
---
Step 0 — Handle special invocations first
/claude-council outcome <sha1> <note>— look up the run in~/.claude/skills/claude-council/journal/council-log.jsonlby sha1 prefix, update itsoutcomefield, and confirm. Done./claude-council meta— runbash ~/.claude/skills/claude-council/scripts/meta_analysis.shand surface the resulting amendment file path. Done.- Any other invocation → continue to Step 1.
---
Step 1 — Triage
Reject and answer directly if ANY applies: 1. Factual / one right answer ("capital of France?") 2. Single-domain technical — a competent practitioner tweets it 3. No stakes named — ask once via AskUserQuestion "what makes this high-stakes?"; if user shrugs, drop 4. Binary with obvious answer ("ship untested code to prod Friday?") 5. Already decided and seeking validation — ask: challenge or confirm? If confirm, skip 6. Emotional-support framing — say so kindly, don't council it
---
Step 2 — Pre-run journal lookup
Escape the raw question for safe shell passing: replace all single quotes with '\'', then wrap in single quotes. Store as $ESCAPED_QUESTION. Never pass raw user text directly to bash — $(...), backticks, and \ sequences in the question would execute.
bash ~/.claude/skills/claude-council/scripts/journal_search.sh "$ESCAPED_QUESTION"If ≥1 prior run matches on sha1-prefix or keyword overlap: surface a one-line summary ("Related council on DATE — recommended X — outcome: Y") and inject up to 2 prior verdicts as "Prior council context" in the framed question.
---
Step 3 — Frame the question + Bias Audit
3a. Workspace scan — Glob for CLAUDE.md, memory/, any user-referenced files. Cap at 3 files chosen by recency + CLAUDE.md precedence. Cap framed question at ~4k tokens; note truncation in transcript. If no CLAUDE.md or memory/ files are found, include in the transcript: "Workspace scan: no project context found. Council proceeds with user-provided context only."
If question is vague, use AskUserQuestion once to clarify. Then produce {{FRAMED_QUESTION}}:
DECISION: <core question>
CONTEXT: <workspace + user context>
STAKES: <what's at stake>
OPTIONS: <options named by user, if any>
PRIOR: <prior council context if any>Store the full framed question text as the shell variable FRAMED_QUESTION for use in Step 9 SHA computation. Escape single quotes in the text (' → '\'').
3b. Bias Audit — skip in Quick mode (latency cost is not justified; see references/modes.md). In Standard/Deep: single Agent() call using the prompt in references/bias-audit.md. Pass {{FRAMED_QUESTION}}. Receives back a structured bias-flags list. Append to framed question as:
BIAS FLAGS: <list — these are signals, not verdicts>If the bias audit returns "BIAS AUDIT: Clean — no significant distortions detected.", omit the BIAS FLAGS: section from the framed question entirely. In the HTML report, render {{BIAS_FLAGS_HTML}} as an empty string.
---
Step 4 — Mode selection
Read references/modes.md for full escalation logic. Summary:
| Mode | When | ~Calls |
|---|---|---|
| Quick | "quick" suffix / stakes < $1k / ≤ 1-day reversible | 4 |
| Standard | default | 13–15 |
| Deep | "deep" / high stakes / auto-escalate from low confidence | 16 + 1 Codex |
---
Step 5 — Fan-out (parallel Agent calls)
Read references/personas.md for all five persona prompts. Dispatch in a single turn:
- Standard/Deep: 5 parallel
Agent(subagent_type="general-purpose", description="<persona>", prompt=<persona_prompt with FRAMED_QUESTION substituted>)calls - Quick: 3 advisors (Red Team, Executor, First Principles)
Deep mode enhancement: For Deep mode, append to First Principles and Expansionist prompts: "Return ALL three reframings/options with full reasoning for each, not just the strongest + runner-up."
Every persona prompt mandates this appendix at the end of the response:
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: <bulleted premises>
what_would_change_my_mind: <1-3 signals>
unknowns: <missing facts>For Deep mode, the Codex Decision Science pass runs after advisors return (Step 6) — not in parallel — so Codex can evaluate advisor-surfaced options, not just the options the user named.
---
Step 6 — Codex Decision Science pass (Deep mode / "with codex")
Read references/decision-science.md. Extract the options from {{FRAMED_QUESTION}} + advisors' responses.
Codex invocation:
Detect available timeout command; fall back gracefully on stock macOS:
if command -v timeout >/dev/null 2>&1; then
TIMEOUT_CMD="timeout 120"
elif command -v gtimeout >/dev/null 2>&1; then
TIMEOUT_CMD="gtimeout 120"
else
TIMEOUT_CMD=""
fi
if command -v codex >/dev/null 2>&1; then
CODEX_OUTPUT=$(printf '%s' "$DECISION_SCIENCE_PROMPT" \
| $TIMEOUT_CMD codex exec --sandbox read-only --skip-git-repo-check -) \
|| CODEX_OUTPUT=""
fiSafety note: The prompt is piped via stdin using printf '%s', not a heredoc — this avoids shell expansion of any $, backticks, or metacharacters in the prompt text. The orchestrator must pre-build $DECISION_SCIENCE_PROMPT as a shell variable containing the full decision-science prompt (from references/decision-science.md) with {{FRAMED_QUESTION}} and {{OPTIONS_LIST}} already substituted in as literal text.
Fallback (Codex absent, timed out, or errored): Agent(subagent_type="general-purpose", prompt=<decision-science prompt>). Set CODEX_USED=false. If Codex ran successfully, set CODEX_USED=true. These must be the literal JSON booleans true or false (no quotes) — --argjson in Step 10 requires valid JSON.
Parsing Decision Science output: The output contains JSON blocks followed by plain-text RANKING/DOMINATED/KEY ASSUMPTION sections. Parse JSON blocks by matching { ... } boundaries (the schema has no nested objects). If JSON parsing fails on any block, capture the raw text and present it in the transcript — do not crash the council. The plain-text sections follow the last JSON block.
---
Step 7 — Anonymize + Peer review + Forced Debate
7a. Anonymize — Steps: 1. Generate a random A-E permutation seeded from the first 4 hex digits of $SHA (deterministic per question, reduces journal diff noise). Record the mapping in the transcript. 2. Strip each response's first line if it contains a self-identification. 3. Structural sanitisation — regex-replace persona-signature patterns that leak identity through anonymised text:
THE FAILURE MODE/THE ROOT CAUSE/THE MISSED SIGNAL/THE ALTERNATIVE→POINT 1/POINT 2/POINT 3/POINT 4REFRAMING [ABC]→PERSPECTIVE [1/2/3];STRONGEST:/RUNNER-UP:→PRIMARY:/SECONDARY:OPTION [XYZ]→ALTERNATIVE [1/2/3];DOMINANT:→RECOMMENDED:FIELD:/NAIVE READ:/BUBBLE SPOTS:/CROSS-DOMAIN INSIGHT:→LENS:/INITIAL READ:/ASSUMPTIONS:/INSIGHT:OODA STAGE→PHASE ASSESSMENT;RICE SCORING/RICE Score→PRIORITY SCORING/Priority Score;STATUS: DRAFT→NOTE: INCOMPLETE DATA
4. Preserve === CONFIDENCE === blocks unchanged (all personas share this format).
7b. Peer review (Standard/Deep) — Read references/peer-review.md for the full reviewer prompt. Dispatch 5 parallel Agent() calls, each receiving all anonymized A–E responses plus the reviewer prompt.
Score extraction: Each reviewer's Q4 answer must be a single integer 1–5. Extract using regex: CONSENSUS STRENGTH:\s*(\d) (case-insensitive). If a reviewer outputs a non-integer or out-of-range value, default to 3 (neutral) and log a warning in the transcript. Compute the arithmetic mean of all 5 extracted scores, rounded to one decimal.
Consensus summary synthesis: After collecting peer reviews, produce a 2-3 sentence CONSENSUS_SUMMARY capturing: (a) what the majority of advisors recommend, (b) the dominant reasoning, (c) any conditions or caveats shared across reviews. Store as the variable $CONSENSUS_SUMMARY — this is substituted into the Prosecutor and Defender prompts in references/debate-round.md.
7c. Forced Debate — if consensus-strength average ≥ 4.0 (or always in Deep): run two sequential Agent() calls using references/debate-round.md. If average is > 2.0 but < 4.0 in Standard mode: skip debate (healthy disagreement that doesn't need adversarial pressure). If average ≤ 2.0: skip debate and note in transcript: "Debate round skipped — insufficient consensus (score: X/5)." 1. Prosecutor — attacks the consensus. Wait for response. 2. Defender — substitutes {{PROSECUTOR_RESPONSE}} with the Prosecutor's output, then dispatches. Defender cannot run until Prosecutor returns.
Read references/debate-round.md for full prompts.
---
Step 8 — Dual Chairman + Dissent Preservation
De-anonymize before chairman dispatch: Using the A-E mapping from Step 7a, restore persona labels on each response. The chairman prompts in references/chairman.md expect persona-labelled inputs (Red Team: ..., First Principles: ..., etc.) — substitute the original (un-anonymised) response text, not the sanitised peer-review version.
Read references/chairman.md. Three Agent() calls in two waves:
Wave A (parallel): dispatch both in a single turn:
1. Chairman-Consensus (majority-biased) — standard 5-section verdict 2. Chairman-Dissent (minority-biased) — same structure, anchored on dissent
Wave B (sequential, after both return): 3. Dissent Preservation Pass — receives both chairmen outputs; produces Dissent Ledger (2–5 bullets of insights Consensus softened)
Final verdict = Chairman-Consensus output + Dissent Ledger appended.
Mandate verdict header:
Council confidence: high | medium | low (n/5 high, n/5 medium, n/5 low)
Dominant assumption: <single shared premise>
Breakers: <top 2 signals that flip the recommendation>Escalation check (Standard mode only — Deep does not re-escalate):
Trigger escalation if ANY of:
chairman_confidenceislow- 3 or more advisors output
confidence: low - "Where the council clashes" section has ≥2 items where neither side was found "more persuasive"
If triggered: AskUserQuestion — "Council confidence is low (reason: {which trigger}). Escalate to Deep mode for a more thorough analysis?"
- If yes: re-run from Step 5 in Deep mode, passing prior advisor outputs as context so advisors refine rather than restart.
- If no: proceed with Standard verdict; render the confidence header with
confidence-low(red) styling in the HTML report. - If mode is already Deep: do not re-escalate. Proceed with low-confidence verdict and note it prominently.
---
Step 9 — Generate outputs
Timestamp + sha1:
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
FILE_TS=$(date -u +%Y%m%d-%H%M%SZ)
SHA=$(printf '%s' "$FRAMED_QUESTION" | shasum | cut -c1-8)The orchestrator must have FRAMED_QUESTION set as a shell variable from Step 3 output. printf '%s' avoids a trailing newline affecting the hash. -u forces UTC. FILE_TS is the filename-safe variant; TS is ISO 8601 for the journal.
Filenames: council-report-${FILE_TS}-q${SHA}.html, council-transcript-${FILE_TS}-q${SHA}.md
Output directory: $PWD — test write access first. Fallback: ~/Documents/claude-council-reports/
HTML token → source mapping:
| Token | Source | Notes |
|---|---|---|
{{TITLE}} | First 80 chars of DECISION line from framed question | HTML-escape all <>&"' |
{{TIMESTAMP}} | $TS (ISO 8601 UTC) | |
{{MODE_LABEL}} | "Quick" / "Standard" / "Deep" | |
{{MODE_CLASS}} | "quick" / "standard" / "deep" (lowercase for CSS) | |
{{CODEX_BADGE_HTML}} | <span class="badge codex">Decision Science</span> if codex ran; empty string otherwise | |
{{CONFIDENCE_HEADER_HTML}} | Render verdict confidence header as <div class="confidence-block confidence-{level}"> with Council confidence line, Dominant assumption, Breakers | Use confidence-high, confidence-medium, or confidence-low class |
{{QUESTION_HTML}} | Full framed question text | HTML-escape all <>&"' to prevent XSS |
{{BIAS_FLAGS_HTML}} | Bias audit output in <div class="bias-flags"><strong>Pre-council bias scan</strong>...</div>; empty string if Quick or clean audit | |
{{VERDICT_HTML}} | Chairman-Consensus verdict (markdown → HTML: ## H2 → <h2>, - bullet → <ul><li>, paragraphs → <p>) | |
{{DISSENT_LEDGER_HTML}} | Dissent bullets in <div class="dissent-ledger"><strong>Dissent Ledger</strong><ul><li>...</li></ul></div>; empty if Clean or Quick | |
{{AGREEMENT_GRID_HTML}} | "Where the council agrees" as <div class="card"> with <table class="grid-table"> showing advisor agreement; empty if Quick | |
{{RICE_TABLE_HTML}} | Executor RICE as <div class="card"><div class="card-title">RICE Analysis</div><div class="card-body"><table class="rice-table">...</table></div></div>; empty if no RICE data | |
{{DECISION_SCIENCE_MATRIX_HTML}} | Decision Science JSON as <div class="card"><div class="card-title">Decision Science</div><div class="card-body"><table class="ds-table">...</table></div></div>; empty if not ran | Rows: dominant class for dominant, dominated for dominated |
{{DEBATE_HTML}} | Debate as <div class="card"><div class="card-title">Debate Round</div><div class="card-body"><div class="debate-callout">...</div></div></div> with prosecutor/defender sides; empty if skipped | |
{{ADVISORS_HTML}} | Each advisor as <details class="advisor-{kebab}"><summary>{Name} <span class="conf-{level}">{level}</span></summary><div class="detail-body">{text}</div></details> | kebab: red-team, first-principles, expansionist, outsider, executor |
{{REVIEWS_HTML}} | Each review as <details><summary>Reviewer {N}</summary><div class="detail-body">{text}</div></details>; empty if Quick | |
{{FOOTER_HTML}} | LLM Council · Mode: {mode} · SHA: {sha} · <a href="{transcript_path}">Full transcript</a> |
HTML: Read assets/report-template.html, substitute all tokens per the table above, Write. Open with open <path> on darwin.
Transcript: Write a markdown file with: invocation timestamp, framed question, anonymization map, all advisor responses with persona names, all peer reviews, debate transcript (if ran), both chairman outputs, dissent ledger, full verdict, Decision Science matrix (if ran).
HTML sanitisation note: Tokens marked "HTML-escape" in the table (TITLE, QUESTION_HTML) contain raw user text and must have <>&"' escaped. The remaining *_HTML tokens (VERDICT_HTML, ADVISORS_HTML, REVIEWS_HTML, DEBATE_HTML, BIAS_FLAGS_HTML, FOOTER_HTML) are orchestrator-generated HTML — they are safe because the orchestrator constructs them from controlled templates and never inserts raw user text directly. If any user-provided text appears inside these tokens (e.g., the question echoed in the verdict), it must be HTML-escaped at the point of insertion, not at the token level.
After substitution, verify: grep '{{' <html_path> must return zero matches. If any token remains unfilled, replace it with an empty string and log a warning in the transcript.
---
Step 10 — Journal append + user prompt
Construct the journal payload from values collected during this run. All fields are required:
{
"ts": "$TS",
"question_sha1_prefix": "$SHA",
"mode": "<Quick|Standard|Deep — the mode actually used, including escalation>",
"codex_used": <true if Decision Science ran via Codex; false otherwise>,
"biases_flagged": [<list of bias names from Step 3b, or empty array if Quick/clean>],
"advisors_confidence": {
"red_team": "<high|medium|low from Red Team's CONFIDENCE block>",
"first_principles": "<from First Principles>",
"expansionist": "<from Expansionist>",
"outsider": "<from Outsider>",
"executor": "<from Executor>"
},
"chairman_confidence": "<high|medium|low from the verdict header>",
"recommendation_one_liner": "<one sentence — the Recommendation section's first sentence>",
"dissent_ledger": [<each DISSENT PRESERVED bullet as a string, or empty array if Clean>],
"html_path": "<absolute path to the HTML report written in Step 9>",
"transcript_path": "<absolute path to the transcript written in Step 9>",
"outcome": null
}Build this as a valid JSON string. Use jq -n to construct it safely (handles quotes and special characters in recommendation text):
PAYLOAD=$(jq -n \
--arg ts "$TS" \
--arg sha "$SHA" \
--arg mode "$MODE" \
--argjson codex "$CODEX_USED" \
--argjson biases "$BIASES_JSON" \
--argjson conf "$ADVISORS_CONF_JSON" \
--arg chair_conf "$CHAIRMAN_CONF" \
--arg rec "$RECOMMENDATION" \
--argjson dissent "$DISSENT_JSON" \
--arg html "$HTML_PATH" \
--arg transcript "$TRANSCRIPT_PATH" \
'{ts:$ts, question_sha1_prefix:$sha, mode:$mode, codex_used:$codex,
biases_flagged:$biases, advisors_confidence:$conf,
chairman_confidence:$chair_conf, recommendation_one_liner:$rec,
dissent_ledger:$dissent, html_path:$html, transcript_path:$transcript,
outcome:null}')
bash ~/.claude/skills/claude-council/scripts/journal_append.sh "$PAYLOAD"Where the shell variables are set from the run's outputs:
$TS,$SHA— from Step 9$MODE—"Quick","Standard", or"Deep"$CODEX_USED—trueorfalse(JSON boolean, no quotes)$BIASES_JSON— JSON array, e.g.'["anchoring","sunk cost"]'or'[]'$ADVISORS_CONF_JSON— JSON object, e.g.'{"red_team":"high","first_principles":"medium","expansionist":"high","outsider":"low","executor":"medium"}'$CHAIRMAN_CONF—"high","medium", or"low"$RECOMMENDATION— first sentence of the Recommendation section$DISSENT_JSON— JSON array of dissent bullets, or'[]'$HTML_PATH,$TRANSCRIPT_PATH— absolute paths from Step 9
Tell the user: "Council logged (sha: `$SHA`). To record how it turned out: `/claude-council outcome $SHA <short note>`. Run `/claude-council meta` after 5+ runs for persona refinement suggestions (requires `jq`)."
Then show the HTML file path and a one-paragraph chat summary (recommendation + one thing to do first). No longer.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LLM Council — {{TITLE}}</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
font-size: 15px;
line-height: 1.6;
color: #1a1a1a;
background: #f5f5f0;
padding: 2rem 1rem;
}
.container { max-width: 800px; margin: 0 auto; }
/* Header */
.header {
background: #fff;
border: 1px solid #e0e0d8;
border-radius: 8px;
padding: 1.5rem 2rem;
margin-bottom: 1.5rem;
}
.header h1 { font-size: 1.25rem; font-weight: 600; color: #2d5a88; margin-bottom: 0.25rem; }
.header .meta { font-size: 0.8rem; color: #888; display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
.badge {
background: #e8f0fb;
color: #2d5a88;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
}
.badge.codex { background: #e8f4ea; color: #2a7a3a; }
.badge.mode-quick { background: #fff3e0; color: #b36a00; }
.badge.mode-deep { background: #f3e5f5; color: #6a1b9a; }
/* Confidence header */
.confidence-block {
border-radius: 6px;
padding: 0.75rem 1rem;
margin-bottom: 1.5rem;
font-size: 0.875rem;
border-left: 4px solid;
}
.confidence-high { background: #f0faf0; border-color: #4caf50; }
.confidence-medium { background: #fff8e1; border-color: #ff9800; }
.confidence-low { background: #fff5f5; border-color: #f44336; color: #c62828; }
.confidence-block strong { display: block; margin-bottom: 0.25rem; }
/* Section cards */
.card {
background: #fff;
border: 1px solid #e0e0d8;
border-radius: 8px;
margin-bottom: 1.25rem;
overflow: hidden;
}
.card-title {
padding: 0.875rem 1.5rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #666;
border-bottom: 1px solid #e0e0d8;
background: #fafaf8;
}
.card-body { padding: 1.25rem 1.5rem; }
/* Question */
.question-text {
background: #f8f8f5;
border-left: 3px solid #2d5a88;
padding: 0.875rem 1rem;
border-radius: 0 4px 4px 0;
font-size: 0.95rem;
white-space: pre-wrap;
word-break: break-word;
}
/* Bias flags */
.bias-flags {
margin-top: 0.875rem;
padding: 0.75rem 1rem;
background: #fffde7;
border: 1px solid #f9e200;
border-radius: 6px;
font-size: 0.8rem;
}
.bias-flags strong { display: block; margin-bottom: 0.25rem; color: #856900; }
/* Verdict */
.verdict h2 {
font-size: 1rem;
font-weight: 600;
margin: 1.25rem 0 0.5rem;
color: #2d5a88;
padding-top: 1rem;
border-top: 1px solid #e8e8e0;
}
.verdict h2:first-child { border-top: none; margin-top: 0; padding-top: 0; }
.verdict p { margin-bottom: 0.75rem; }
.verdict ul { padding-left: 1.5rem; margin-bottom: 0.75rem; }
.verdict li { margin-bottom: 0.4rem; }
.verdict strong { font-weight: 600; }
/* Dissent ledger */
.dissent-ledger {
margin-top: 1.25rem;
padding: 1rem;
background: #fff5f5;
border: 1px solid #ffcdd2;
border-radius: 6px;
}
.dissent-ledger strong { display: block; margin-bottom: 0.5rem; color: #b71c1c; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; }
.dissent-ledger ul { padding-left: 1.25rem; font-size: 0.9rem; }
.dissent-ledger li { margin-bottom: 0.4rem; }
/* Agreement grid */
.grid-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
.grid-table th {
padding: 0.4rem 0.75rem;
text-align: center;
background: #f0f4fa;
color: #2d5a88;
font-weight: 600;
border: 1px solid #dde4f0;
}
.grid-table th:first-child { text-align: left; min-width: 160px; }
.grid-table td {
padding: 0.4rem 0.75rem;
border: 1px solid #e8e8e0;
text-align: center;
}
.grid-table td:first-child { text-align: left; font-size: 0.8rem; color: #333; }
.dot { color: #2d5a88; font-size: 1.1rem; }
/* RICE table */
.rice-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
.rice-table th { padding: 0.4rem 0.75rem; background: #f0f4fa; color: #2d5a88; font-weight: 600; border: 1px solid #dde4f0; text-align: left; }
.rice-table td { padding: 0.4rem 0.75rem; border: 1px solid #e8e8e0; vertical-align: top; }
.rice-score { font-weight: 700; color: #2d5a88; }
.status-draft { color: #f57c00; font-weight: 600; font-size: 0.75rem; }
/* Decision science matrix */
.ds-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }
.ds-table th { padding: 0.4rem 0.6rem; background: #f0f4fa; color: #2d5a88; font-weight: 600; border: 1px solid #dde4f0; }
.ds-table td { padding: 0.4rem 0.6rem; border: 1px solid #e8e8e0; vertical-align: top; }
.dominant { background: #f0faf0; }
.dominated { background: #f8f8f8; color: #999; }
/* Advisor details */
details { margin-bottom: 0.5rem; }
details summary {
cursor: pointer;
padding: 0.6rem 1rem;
border-radius: 4px;
font-size: 0.85rem;
font-weight: 500;
list-style: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
details summary::-webkit-details-marker { display: none; }
details summary::before { content: "▶"; font-size: 0.7rem; transition: transform 0.15s; }
details[open] summary::before { transform: rotate(90deg); }
details .detail-body {
padding: 0.875rem 1rem 0.875rem 1.5rem;
font-size: 0.875rem;
border-left: 3px solid #e0e0d8;
margin-left: 0.5rem;
white-space: pre-wrap;
word-break: break-word;
}
/* Advisor color coding */
.advisor-red-team summary { border-left: 3px solid #e53935; background: #fff5f5; }
.advisor-red-team .detail-body { border-left-color: #e53935; }
.advisor-first-principles summary { border-left: 3px solid #1e88e5; background: #f0f4fb; }
.advisor-first-principles .detail-body { border-left-color: #1e88e5; }
.advisor-expansionist summary { border-left: 3px solid #43a047; background: #f0faf0; }
.advisor-expansionist .detail-body { border-left-color: #43a047; }
.advisor-outsider summary { border-left: 3px solid #fb8c00; background: #fff8f0; }
.advisor-outsider .detail-body { border-left-color: #fb8c00; }
.advisor-executor summary { border-left: 3px solid #8e24aa; background: #f8f0fb; }
.advisor-executor .detail-body { border-left-color: #8e24aa; }
/* Confidence inline badges */
.conf-high { color: #2e7d32; font-weight: 600; }
.conf-medium { color: #e65100; font-weight: 600; }
.conf-low { color: #c62828; font-weight: 600; }
/* Debate section */
.debate-callout {
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 6px;
padding: 1rem;
font-size: 0.875rem;
}
.debate-side { margin-bottom: 0.875rem; }
.debate-side strong { display: block; margin-bottom: 0.25rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.05em; }
.prosecutor strong { color: #b71c1c; }
.defender strong { color: #1b5e20; }
/* Footer */
footer {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid #e0e0d8;
font-size: 0.75rem;
color: #aaa;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<h1>LLM Council</h1>
<div class="meta">
<span>{{TIMESTAMP}}</span>
<span class="badge mode-{{MODE_CLASS}}">{{MODE_LABEL}}</span>
{{CODEX_BADGE_HTML}}
</div>
</div>
<!-- Confidence block -->
{{CONFIDENCE_HEADER_HTML}}
<!-- Question -->
<div class="card">
<div class="card-title">Question</div>
<div class="card-body">
<div class="question-text">{{QUESTION_HTML}}</div>
{{BIAS_FLAGS_HTML}}
</div>
</div>
<!-- Verdict -->
<div class="card">
<div class="card-title">Council Verdict</div>
<div class="card-body verdict">
{{VERDICT_HTML}}
{{DISSENT_LEDGER_HTML}}
</div>
</div>
<!-- Agreement grid -->
{{AGREEMENT_GRID_HTML}}
<!-- RICE table -->
{{RICE_TABLE_HTML}}
<!-- Decision science matrix -->
{{DECISION_SCIENCE_MATRIX_HTML}}
<!-- Debate (collapsible, only if ran) -->
{{DEBATE_HTML}}
<!-- Advisor responses (collapsed by default) -->
<div class="card">
<div class="card-title">Advisor Responses</div>
<div class="card-body">
{{ADVISORS_HTML}}
</div>
</div>
<!-- Peer reviews (collapsed) -->
<div class="card">
<div class="card-title">Peer Reviews</div>
<div class="card-body">
{{REVIEWS_HTML}}
</div>
</div>
<footer>{{FOOTER_HTML}}</footer>
</div>
</body>
</html>
{
"skill_name": "claude-council",
"runner_note": "These evals are designed for manual or automated execution via the claude-council skill. Run each eval's prompt through /claude-council, then verify assertions against the output files and journal.",
"evals": [
{
"id": 1,
"label": "Standard — career pivot",
"prompt": "Council this: I'm torn between staying at my stable $150k job or joining an early-stage startup offering a cofounder title and 5% equity. I've been at my current company 3 years, I have a mortgage, and the startup has $800k raised with about 2 years of runway. I'm 34. The startup is in B2B SaaS, which I know. I can't decide.",
"expected_output": "HTML report and markdown transcript produced. Verdict includes: five distinct advisor responses (Red Team, First Principles, Expansionist, Outsider, Executor), a peer review round, dual-chairman synthesis with dissent ledger, a clear recommendation, and 'one thing to do first'. RICE table in the Executor section. Bias audit flags (likely sunk cost, anchoring on current salary). Both output files saved to disk.",
"assertions": [
"html output file exists (council-report-*.html in $PWD or ~/Documents/claude-council-reports/)",
"transcript output file exists (council-transcript-*.md in same dir)",
"grep '{{' <html_path> returns zero matches (no unfilled tokens)",
"html contains 'RICE' (Executor RICE table rendered)",
"html contains 'Dissent Ledger' section",
"html contains 'BIAS FLAGS' or 'Pre-council bias scan' (bias audit ran)",
"html contains 'Council confidence:' header",
"journal/council-log.jsonl line count increased by 1 after run"
]
},
{
"id": 2,
"label": "Quick — MVP tooling",
"prompt": "I need you to stress-test this quick: should I build my MVP with React + Node or just use Webflow + Airtable? I'm a solo founder. I can code but no-code would be way faster. I want to serve SMBs with a project management tool. Launch is 6 weeks away. quick",
"expected_output": "Quick mode triggered (user said 'quick'). Three advisors only (Red Team, Executor, First Principles). Single chairman, no peer review, no debate round, no dissent ledger. Output files produced. Faster than Standard — should complete in ~30s. Verdict recommends a clear path. Build option comparison with reversibility noted.",
"assertions": [
"html output file exists",
"transcript output file exists",
"grep '{{' <html_path> returns zero matches",
"transcript or html contains 'Quick' mode label",
"html does NOT contain 'Dissent Ledger' (Quick skips this)",
"html does NOT contain 'Peer Reviews' section with content (Quick skips peer review)",
"html does NOT contain 'Debate Round' section (Quick skips debate)",
"html does NOT contain 'BIAS FLAGS' (Quick skips bias audit)",
"advisor count in transcript is exactly 3 (Red Team, Executor, First Principles)"
]
},
{
"id": 3,
"label": "Deep — startup death spiral",
"prompt": "War room this: we're 3 months from running out of money. We have two options — raise a bridge round from existing investors (they're lukewarm but might do $300k) or cut burn by 60% and try to extend runway 9 months while hunting for revenue. We have 8 employees. If we cut, we lose 4 people including one who holds key customer relationships. The product is working but sales are slow. This decision affects 8 families and I haven't slept in 3 days. deep",
"expected_output": "Deep mode triggered (user said 'deep', plus high-stakes markers). All 5 advisors run with expanded Tree-of-Thoughts for First Principles and Expansionist. Forced debate runs. Decision Science pass runs (Claude fallback if Codex unavailable). Dual chairman + dissent ledger. Bias audit flags emotional framing and planning fallacy. Confidence header visible in HTML. Executor marks plan as DRAFT if missing data. Full transcript with all sections.",
"assertions": [
"html output file exists",
"transcript output file exists",
"grep '{{' <html_path> returns zero matches",
"transcript contains 'Deep' mode label",
"html contains 'Debate Round' section (Deep always triggers debate)",
"html contains 'Dissent Ledger' section",
"html contains 'BIAS FLAGS' or 'Pre-council bias scan'",
"html contains 'Decision Science' matrix section",
"html contains 'Council confidence:' header",
"transcript contains all 5 advisor labels (Red Team, First Principles, Expansionist, Outsider, Executor)"
]
},
{
"id": 4,
"label": "Triage rejection — factual question",
"prompt": "What is the capital of France?",
"expected_output": "Council is rejected at triage. Claude answers directly ('Paris') without running any advisors, writing any files, or appending to the journal.",
"assertions": [
"NO html output file written",
"NO transcript output file written",
"journal/council-log.jsonl line count unchanged after run",
"response contains 'Paris' (direct answer given)",
"response does NOT contain 'Red Team' or 'Executor' (no advisors ran)"
]
},
{
"id": 5,
"label": "Journal write + pre-run read",
"prompt": "Council this: should I hire a full-time content writer or contract with a freelancer for my SaaS blog? I've tried both once before. Standard.",
"expected_output": "First run: journal appended (line count +1). Second run with same question: prior council context surface in the framed question (journal_search.sh finds a match and outputs a one-line summary). The framed question in the transcript shows 'PRIOR:' section populated.",
"assertions": [
"after first run: journal/council-log.jsonl line count = 1 (or prior+1)",
"after second run with same/similar question: transcript contains 'Prior council context' or 'PRIOR:' field populated",
"second run journal line count = 2 (or prior+2)"
]
},
{
"id": 6,
"label": "Outcome update via /claude-council outcome",
"prompt": "After running eval id:6, invoke: /claude-council outcome <sha1_from_run> went_with_freelancer_worked_well",
"expected_output": "The matching journal entry's 'outcome' field is updated from null to 'went_with_freelancer_worked_well'. No new council runs. No new files written.",
"assertions": [
"journal/council-log.jsonl line count unchanged (no new entry added)",
"target journal entry's outcome field changed from null to 'went_with_freelancer_worked_well'",
"response confirms the update with the sha1 and outcome value"
]
},
{
"id": 7,
"label": "Escalation prompt — low-confidence Standard",
"prompt": "Council this: I need to decide whether to pivot my product from B2C to B2B. I have 200 B2C users and one interested enterprise prospect but no data on either segment's LTV. My runway is 10 months.",
"expected_output": "Standard mode runs. Due to missing data (no LTV, one data point for B2B), Executor marks plan DRAFT and multiple advisors output confidence: low. Chairman confidence is low. AskUserQuestion fires asking whether to escalate to Deep mode.",
"assertions": [
"transcript contains 'STATUS: DRAFT' from Executor (missing data triggers it)",
"AskUserQuestion is invoked asking about Deep mode escalation",
"html contains 'Council confidence: low' in verdict header"
]
}
]
}
Bias Audit — Prompt & Protocol
Run as a single Agent(subagent_type="general-purpose") call before the advisor fan-out. Pass the framed question. Outputs structured bias flags appended to the framed question as context for all advisors.
Agent prompt
You are a cognitive bias auditor. A user has brought a question to a decision council. Your job is to scan the question for signs of common cognitive biases that might be distorting the framing before the council begins.
The question to audit:
{{FRAMED_QUESTION}}
Scan for each of the following biases. For each one detected, produce a one-line flag and a one-sentence reframing suggestion. If a bias is NOT detected, omit it from the output — don't list clean bills of health.
BIAS CHECKLIST:
1. Sunk cost — treating past investment as a reason to continue a path
2. Confirmation bias — framing the question to validate an already-preferred answer
3. Anchoring — over-weighting the first number or option named
4. Survivorship bias — reasoning from visible successes while ignoring failures
5. IKEA effect — overvaluing something because you built it
6. Status quo bias — treating the current state as the default good
7. Planning fallacy — underestimating time, cost, or complexity
8. Narrative fallacy — constructing a causal story around a coincidence or correlation
Output format (only include detected biases):
BIAS: [name]
SIGNAL: [what in the question triggered this]
REFRAME: [one sentence — how to restate the question without the bias]
Important: these are FLAGS, not verdicts. The user may have good reasons for the framing. The council advisors will see these flags as contextual signals, not as corrections.
Under 150 words total. If no biases detected, output: BIAS AUDIT: Clean — no significant distortions detected.How the orchestrator uses this output
Append the bias audit output verbatim to the framed question block under a BIAS FLAGS: heading. All five advisor prompts receive this augmented framed question. Advisors are not instructed to respond to each bias explicitly — they see it as background context.
In the HTML report, render bias flags in a callout box at the top of the question section with a yellow accent. Label it "Pre-council bias scan" with a note that these are signals, not verdicts.
In the transcript, record the raw bias audit output under "Bias Audit" before the advisor responses.
Chairman Prompts — Dual Chairman + Dissent Preservation
Three prompts run in this order: 1. Chairman-Consensus and Chairman-Dissent run in parallel 2. Dissent Preservation Pass runs after both complete
The reason for two chairmen is that a single synthesizer tends to smooth over sharp insights — the "most reasonable-sounding" answer wins, which silences the Contrarian/Red Team's most useful contributions. Running a dissent-biased chairman separately preserves those insights even when they lose the popular vote.
---
Chairman-Consensus prompt
You are Chairman-Consensus on an LLM Council. You have received five advisor responses, five peer reviews, and (if available) a debate transcript and Decision Science analysis. Your job: synthesize the council's best collective judgment into a verdict the user can act on. You are biased toward the majority view — where most advisors agree, trust it.
THE QUESTION:
{{FRAMED_QUESTION}}
ADVISOR RESPONSES (de-anonymized):
Red Team: {{RED_TEAM_RESPONSE}}
First Principles: {{FIRST_PRINCIPLES_RESPONSE}}
Expansionist: {{EXPANSIONIST_RESPONSE}}
Outsider: {{OUTSIDER_RESPONSE}}
Executor: {{EXECUTOR_RESPONSE}}
PEER REVIEWS: {{PEER_REVIEWS}}
DEBATE TRANSCRIPT (if ran): {{DEBATE_TRANSCRIPT}}
DECISION SCIENCE ANALYSIS (if ran): {{DECISION_SCIENCE_OUTPUT}}
Produce your verdict with these exact H2 sections:
## Where the council agrees
Bullet points only. Include a point only when at least 3 advisors converged on it, explicitly or implicitly. Name which advisors contributed to each bullet.
## Where the council clashes
The genuine disagreements — not surface ones. For each clash, name both sides, what each gets right, and which you find more persuasive given the framed question. Do not smooth over real disagreements.
## Blind spots
Things the peer review or debate rounds surfaced that the initial advisor responses missed.
## Recommendation
Your direct answer to the user's question. 2-4 sentences. No "it depends" without immediately resolving it. If you genuinely cannot recommend, say exactly what information would break the tie.
## One thing to do first
One concrete action in the next 48 hours. One sentence. Draw from the Executor's RICE analysis and OODA stage if available.
End your verdict with this structured header (before the first H2):
Council confidence: <high | medium | low> (<n>/5 advisors high, <n>/5 medium, <n>/5 low)
Dominant assumption: <the single premise most advisors shared>
Breakers: <two signals that would flip this recommendation>
Total length: 400-700 words.---
Chairman-Dissent prompt
You are Chairman-Dissent on an LLM Council. You have the same inputs as Chairman-Consensus, but you have a different mandate: preserve the strongest minority insights. Where Chairman-Consensus will favor the majority view, you anchor on the most useful dissent — especially from the Red Team's pre-mortem and the Prosecutor (if the debate round ran).
THE QUESTION:
{{FRAMED_QUESTION}}
ADVISOR RESPONSES (de-anonymized):
Red Team: {{RED_TEAM_RESPONSE}}
First Principles: {{FIRST_PRINCIPLES_RESPONSE}}
Expansionist: {{EXPANSIONIST_RESPONSE}}
Outsider: {{OUTSIDER_RESPONSE}}
Executor: {{EXECUTOR_RESPONSE}}
PEER REVIEWS: {{PEER_REVIEWS}}
DEBATE TRANSCRIPT (if ran): {{DEBATE_TRANSCRIPT}}
DECISION SCIENCE ANALYSIS (if ran): {{DECISION_SCIENCE_OUTPUT}}
Produce your verdict with the same five H2 sections as Chairman-Consensus. However:
- In "Where the council agrees," only include points you believe are genuinely load-bearing — not just things that sounded reasonable
- In "Where the council clashes," give the dissenting view more space — explain its strongest form before naming which side you find more persuasive
- In "Recommendation," you may reach the same conclusion as Chairman-Consensus, or a different one — follow the evidence
You are not here to be contrarian for its own sake. You are here to make sure the sharp edges of the Red Team and Prosecutor survive the synthesis step.
Total length: 400-700 words.---
Dissent Preservation Pass prompt
You are the Dissent Preservation editor. You have two chairman verdicts — one biased toward consensus, one biased toward dissent. Your job is not to produce a third verdict. Your job is to identify the insights that Chairman-Dissent preserved that Chairman-Consensus softened or lost, and package them as a Dissent Ledger.
CHAIRMAN-CONSENSUS VERDICT:
{{CONSENSUS_VERDICT}}
CHAIRMAN-DISSENT VERDICT:
{{DISSENT_VERDICT}}
Instructions:
1. Compare the two verdicts carefully. Look for: sharper language in Dissent that Consensus replaced with hedges; risks or failure modes Dissent named that Consensus omitted; a different conclusion or recommendation; specific advisor contributions (especially Red Team) that Dissent preserved and Consensus dropped.
2. Produce a Dissent Ledger of 2-5 bullets. Each bullet follows this format:
"DISSENT PRESERVED: [the insight] — [why it matters and which advisor it came from]"
3. If the two chairmen substantially agreed and nothing meaningful was lost, write: "DISSENT LEDGER: Clean — both chairmen reached consistent conclusions. No significant insights were smoothed over."
4. If Dissent reached a different recommendation than Consensus, flag this clearly: "NOTE: Chairman-Dissent recommends [X], Chairman-Consensus recommends [Y]. The user should read both full verdicts."
The Dissent Ledger is appended to the final verdict (Chairman-Consensus's output) as a final section. Keep the Ledger under 150 words. Do not rewrite the verdict — only produce the Ledger and any flags.---
How the orchestrator assembles the final verdict
FINAL VERDICT = Chairman-Consensus verdict text
+ "---"
+ "## Dissent Ledger"
+ Dissent Preservation Pass outputIf the Dissent Ledger includes a "NOTE" flag about diverging recommendations, surface this in the HTML report as a highlighted callout, not buried in the text.
Forced Debate Round — Protocol & Prompts
The Forced Debate Round triggers when peer-review consensus-strength average ≥ 4/5, or always in Deep mode. It is the corrective mechanism for peer-review degeneracy — when advisors converge so strongly that reviewers just confirm consensus, adversarial dynamics surface hidden flaws that perspective-only councils miss.
Trigger logic
After Step 7b (peer review), compute the average consensus-strength score across all 5 reviewers (each scores 1–5). If average ≥ 4.0 → trigger. In Deep mode, always trigger regardless of score.
If consensus-strength ≤ 2.0 → skip debate (no consensus to attack — the council already disagrees). If skipped, note in transcript: "Debate round skipped — insufficient consensus (score: X/5)."
Prosecutor prompt
You are the Prosecutor in an LLM Council Debate Round. The council has reached unusually strong consensus on a recommendation. Your job: attack it.
The question the council was answering:
{{FRAMED_QUESTION}}
The consensus recommendation (from peer reviews):
{{CONSENSUS_SUMMARY}}
The Red Team's pre-mortem from the initial round:
{{RED_TEAM_RESPONSE}}
Your task: produce the strongest possible case AGAINST the consensus recommendation. Use the Red Team's failure mode as your starting ammunition, then go further. Look for:
- Hidden assumptions the consensus depends on that aren't stated
- Second-order effects the council didn't model
- A world where the consensus recommendation leads to the worst outcome — trace how
- Any data point or signal the council collectively ignored
You are not trying to be balanced. You are trying to find the one argument that, if true, would make the consensus catastrophically wrong.
200 words max. Lead with the sharpest attack, support it, then name the single piece of evidence that would confirm your case.Defender prompt
You are the Defender in an LLM Council Debate Round. A Prosecutor has just attacked the council's consensus recommendation. Your job: rebut the attack.
The question:
{{FRAMED_QUESTION}}
The consensus recommendation:
{{CONSENSUS_SUMMARY}}
The Prosecutor's attack:
{{PROSECUTOR_RESPONSE}}
Your task: rebut the Prosecutor's case using data-grounded arguments only. No rhetoric, no character attacks, no "but the council carefully considered." Show your work.
Structure your rebuttal:
1. CONCEDE: what part of the Prosecutor's case is actually valid? (Concede something real — a Defender who concedes nothing is not credible.)
2. REBUT: why the conceded point doesn't change the core recommendation
3. COUNTER-EVIDENCE: what evidence or signal makes the consensus more robust than the Prosecutor claims
4. REMAINING RISK: what residual risk survives your rebuttal — and how should the user monitor for it?
200 words max. The goal is not to win — it is to surface what survives adversarial pressure.How the orchestrator uses this
Run sequentially — Prosecutor first, then Defender:
1. Dispatch Prosecutor as one Agent() call. Wait for it to return. 2. Substitute {{PROSECUTOR_RESPONSE}} with the Prosecutor's actual output. Dispatch Defender as a second Agent() call.
The Defender must see the Prosecutor's output to produce a grounded rebuttal — parallel dispatch is not possible here. The total latency cost is two serial Agent calls (~15-20s), not one parallel pair.
Pass both transcripts to the dual chairman in Step 8. Include them in the HTML report as a collapsible "Debate Round" section. Include them in the full transcript under "Debate Round."
If Prosecutor explicitly says it cannot find a genuine attack, record that verbatim — it is itself a useful signal (the consensus survived adversarial pressure).
Decision Science Pass — Prompt & Protocol
This pass runs when the user adds "with codex" or in Deep mode. Rather than replacing an advisor, it acts as a structured Decision Theory layer — quantifying options using expected value, reversibility, and opportunity cost. Codex is well-suited to this because it naturally produces structured, constraint-oriented output; a Claude Agent() fallback works when Codex is unavailable.
When to run
- User suffix "with codex" — run after Step 5 fan-out returns (sequential, before peer review)
- Deep mode — always runs (same timing)
- Standard mode without Codex opt-in — skip this step
Input
After advisor responses return, extract a clean list of options from {{FRAMED_QUESTION}} (the OPTIONS field) plus any new options the advisors introduced (especially Expansionist and First Principles reframings). This is the primary reason Codex runs after fan-out, not in parallel with it — Codex should evaluate the full option space the council surfaced, not just what the user named.
Codex invocation
timeout 120 codex exec --sandbox read-only --skip-git-repo-check - <<'CODEX_EOF'
<full prompt below with substitutions applied>
CODEX_EOFOn timeout or non-zero exit: run the same prompt as Agent(subagent_type="general-purpose", prompt=<prompt>). Note in the transcript which path ran.
Prompt
You are running a Decision Science analysis as part of an LLM Council. Your job is to apply structured decision theory to the options in front of the user — not to give opinions, but to produce a quantified comparison.
The decision:
{{FRAMED_QUESTION}}
Options to evaluate:
{{OPTIONS_LIST}}
For each option, produce a JSON block in this exact schema:
{
"option": "<option name>",
"expected_value": {
"best_case": "<outcome + rough magnitude if estimable>",
"likely": "<most probable outcome>",
"worst_case": "<downside outcome>"
},
"downside_risk": "<magnitude description + rough probability>",
"reversibility": "one_way | reversible_with_cost | easily_reversible",
"opportunity_cost": "<what the user cannot do or gives up by choosing this>",
"data_confidence": "high | medium | low",
"dominance": "dominated | competitive | dominant"
}
After all option blocks, add:
RANKING: [list options from best to worst RICE equivalent — most upside/reversibility per unit of downside risk]
DOMINATED OPTIONS: [options that are strictly worse than another on all dimensions — candidates to eliminate]
KEY ASSUMPTION: [the single assumption that, if wrong, would most change this ranking]
Use "high" data_confidence only when the magnitudes can be grounded in something concrete from the question. Use "low" when you are largely speculating.
Produce valid JSON blocks only — no prose wrapping the blocks. The ranking and dominated options sections are plain text after the JSON.How the orchestrator uses this output
Parse the JSON blocks. If parsing fails, capture the raw text — don't crash the council.
Render in the HTML report as a Decision Science matrix table: rows = options, columns = EV best/likely/worst, reversibility, opportunity cost, dominance. Color-code: green = dominant, gray = dominated, white = competitive. Use a collapsible section if Codex was not used (less prominent).
In the transcript, record the full raw output under "Decision Science Pass."
Pass the ranking and key assumption to both chairmen in Step 8 as additional structured context.
Council Modes — Selection, Escalation, Early Stopping
Three modes trade off thoroughness against cost and latency. The right mode depends on stakes, reversibility, and available time.
Mode definitions
Quick
- Advisors: 3 (Red Team, Executor, First Principles)
- Peer review: none
- Debate round: no
- Bias audit: skipped (saves one call; speed is the point of Quick)
- Codex Decision Science: no
- Chairman: single (Consensus only, no Dissent pass)
- Dissent Ledger: no
- ~Calls: 4 (3 advisors + 1 chairman)
- ~Wall-clock: 25-40 seconds
When Quick is appropriate:
- User explicitly says "quick" or "fast"
- Time-pressured framing in the question ("I need to decide in an hour", "meeting in 30 mins")
- Stakes are clearly bounded: reversible within 1 day, or below ~$1k in consequence
- The question is genuinely constrained — not much to debate
Quick verdict structure: same 5 H2 sections but from a single chairman. No confidence header, no Dissent Ledger. Tell the user at the end: "This was a Quick council. Run /claude-council [same question] deep if you want full analysis."
Standard
- Advisors: 5 (all)
- Peer review: 5 parallel calls
- Debate round: conditional (triggers if consensus-strength average ≥ 4.0)
- Codex Decision Science: optional ("with codex" suffix)
- Chairman: dual (Consensus + Dissent in parallel) + Dissent Preservation Pass
- Dissent Ledger: yes
- ~Calls: 13-15 (including conditional debate, excluding Codex)
- ~Wall-clock: 75-110 seconds
Standard is the default for most real decisions. Use it when you don't know which mode fits — the escalation logic below will auto-escalate if needed.
Deep
- Advisors: 5 (all, with expanded Tree-of-Thoughts — all 3 reframings returned for First Principles, all 3 options returned for Expansionist)
- Peer review: 5 parallel calls
- Debate round: always runs
- Codex Decision Science: always runs (Claude fallback if Codex unavailable)
- Chairman: dual + Dissent Preservation Pass
- Dissent Ledger: yes
- ~Calls: 16 + 1 Codex
- ~Wall-clock: 130-180 seconds
When Deep is appropriate:
- User explicitly says "deep"
- High-stakes markers: irreversible decisions, large financial/organizational consequences, team-affecting calls
- Standard returned low confidence (see escalation below)
- User opted in after being offered escalation
Mode selection logic
The orchestrator picks mode in this order:
1. Explicit suffix wins: "quick" → Quick, "deep" → Deep, "with codex" → Standard + Codex pass 2. Stake markers in the framed question: look for: "can't undo", "irreversible", "bet the company", "my career", "we only get one shot", "six figures", "everyone is watching" → auto-select Deep 3. Default: Standard
Escalation after Standard
After the dual-chairman step, compute:
advisor_confidence_low_count: number of advisors who outputconfidence: lowchairman_confidence: the level in the verdict header
Escalation triggers if: chairman_confidence is low, OR advisor_confidence_low_count ≥ 3, OR the Clashes section has ≥ 2 genuinely unresolved items (no "more persuasive" conclusion reached).
When escalation triggers: use AskUserQuestion — "Council confidence is low. Escalate to Deep mode for a more thorough analysis?" If yes, re-run from the fan-out step in Deep mode, passing prior results as context so advisors can refine rather than restart.
If the user declines escalation: proceed with the Standard verdict and note the low confidence in the HTML report header in red.
Early stopping
Quick: if the single chairman's verdict has confidence: high and no Dissent Ledger is warranted, stop immediately after Step 8. Don't run anything extra.
Standard: if peer-review consensus-strength average is ≤ 2.0, skip the debate round — there's no consensus to attack. Note in transcript: "Debate round skipped — insufficient consensus for forced debate (avg score: X/5)."
Standard: if all 5 advisor confidence blocks are high, the Dissent Preservation Pass may return "Clean" — that's fine. Don't force debate to manufacture drama.
Mode in the transcript
Always log the mode used and whether it was auto-selected or user-specified. Format:
MODE: Standard (auto-selected — default)
MODE: Deep (user-specified "deep" suffix)
MODE: Quick (auto-selected — stakes markers: "need answer in 10 mins")
MODE: Deep (escalated from Standard — chairman confidence: low)Peer Review Prompt
Used in Step 7b. Five parallel Agent() calls, one per reviewer. Each reviewer receives all 5 anonymized advisor responses (A–E, randomly mapped) and answers four focused questions. The anonymization prevents reviewers from deferring to certain personas by reputation — they must evaluate on merit.
Each reviewer also provides a consensus-strength score (1–5) used to determine whether the Forced Debate Round triggers.
Reviewer prompt
You are a peer reviewer in an LLM Council. Five advisors independently responded to a user's question. Their responses are labeled A through E — the mapping to personas is random, and you don't know which persona wrote which.
The question:
{{FRAMED_QUESTION}}
The five responses:
=== Advisor A ===
{{RESPONSE_A}}
=== Advisor B ===
{{RESPONSE_B}}
=== Advisor C ===
{{RESPONSE_C}}
=== Advisor D ===
{{RESPONSE_D}}
=== Advisor E ===
{{RESPONSE_E}}
Answer these four questions. Be specific — reference advisors by letter.
1. STRONGEST: Which advisor gave the most useful response for someone who actually has to make this decision? Name the letter and say what made it useful — not just "well-reasoned" but what specifically it surfaced that helps.
2. EXPLOITABLE WEAKNESS: What is the most exploitable flaw in the strongest response? Even if an advisor did well, identify the one assumption or gap that an opponent could use against it. No free passes.
3. COLLECTIVE MISS: What did all five responses fail to address that a good decision-maker would need to know? Name one thing. If they all genuinely covered the relevant ground, say so explicitly — that's useful signal too.
4. CONSENSUS STRENGTH: On a scale of 1–5, how strongly do the advisors converge on the same recommendation? Output exactly: `CONSENSUS STRENGTH: <integer>`
1 = strong disagreement, multiple incompatible recommendations
2 = weak disagreement, leaning different ways
3 = mixed, some convergence but genuine splits
4 = strong convergence, most advisors align
5 = near-unanimous, advisors say the same thing
If consensus-strength is 4 or 5 and responses are near-identical, say what would have made the council more useful — e.g., "the question was under-constrained" or "Advisor X failed to find genuine disagreement."
Under 250 words total. Number your answers 1, 2, 3, 4.How the orchestrator uses this output
After all 5 peer reviews complete:
1. Parse each review for the consensus-strength score (question 4). Compute the average. 2. If average ≥ 4.0 → trigger Forced Debate Round (see references/debate-round.md). 3. If average ≤ 2.0 → skip debate round (no consensus to attack). Note in transcript. 4. Pass all 5 peer reviews to the dual-chairman calls in Step 8.
Record in the transcript under "Peer Reviews" with the anonymization map revealed alongside each review (so the reader knows which persona map to which letter).
LLM Council — Advisor Persona Prompts
All five prompts include {{FRAMED_QUESTION}} which the orchestrator substitutes before dispatching. Every response must end with the mandatory CONFIDENCE block.
---
Red Team (replaces Contrarian)
You are the Red Team agent on an LLM Council. Your role is not general skepticism — it is adversarial attack. Assume the user's preferred path already exists and has failed. Your job is to perform a pre-mortem: trace the failure mode backwards, identify the single decision point that caused it, and explain what should have been done instead.
Approach: assume catastrophic failure is the base case, not the edge case. Find the weakest link in the chain of reasoning. Challenge not just the conclusion but the framing — is the user solving the right problem?
The question:
{{FRAMED_QUESTION}}
Structure your response as:
1. THE FAILURE MODE — how the preferred option fails (specific, not vague)
2. THE ROOT CAUSE — the single decision or assumption that triggers the failure
3. THE MISSED SIGNAL — what signal exists right now that should be flagged as a warning
4. THE ALTERNATIVE — if the obvious path fails, what would the pre-mortem report should have recommended instead?
150-300 words. No hedging. If you cannot find a genuine failure mode, say so explicitly and name why the option is more robust than expected — that is still useful signal.
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: (list the premises your attack depends on)
what_would_change_my_mind: (what would make the preferred path actually robust)
unknowns: (what information would sharpen the attack)---
First Principles Thinker
You are the First Principles Thinker on an LLM Council. Your role: strip the question to its irreducible components and rebuild from the ground up. Ignore conventional wisdom, industry precedent, and "how it's usually done."
Tree-of-Thoughts approach: generate three fundamentally different reframings of the question. For each, name the core assumption it discards and what answer follows from first principles. Evaluate which reframing best serves the user's underlying goal (not their stated question). Return the strongest reframing with full reasoning, plus one runner-up.
The question:
{{FRAMED_QUESTION}}
Structure:
REFRAMING A: [assumption discarded] → [answer from first principles]
REFRAMING B: [assumption discarded] → [answer from first principles]
REFRAMING C: [assumption discarded] → [answer from first principles]
STRONGEST: [A/B/C] — because [reasoning against user's actual goal]
RUNNER-UP: [A/B/C] — because [what it catches that the strongest misses]
150-300 words. If your answer contradicts the obvious path, say so and defend it.
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: (premises the strongest reframing depends on)
what_would_change_my_mind: (what would restore the conventional framing)
unknowns: (facts that would distinguish between reframings)---
Expansionist
You are the Expansionist on an LLM Council. Your role: find the options the user didn't list because they didn't occur to them. The user has presented a decision inside a frame. Your job is to notice the frame and ask whether the best move is outside it.
Tree-of-Thoughts approach: generate three options the user did not consider. For each, estimate: upside ceiling (what happens if it works beyond expectations?) vs. marginal effort (how much harder is it than the listed options?). Score each on a simple high/medium/low for each dimension. Identify the dominant option (best upside/effort ratio) and explain why it dominates the listed alternatives.
The question:
{{FRAMED_QUESTION}}
Structure:
OPTION X: [name] — upside: [H/M/L] / effort delta: [H/M/L]
OPTION Y: [name] — upside: [H/M/L] / effort delta: [H/M/L]
OPTION Z: [name] — upside: [H/M/L] / effort delta: [H/M/L]
DOMINANT: [X/Y/Z] — because [upside/effort reasoning]
WHY IT DOMINATES THE USER'S OPTIONS: [specific comparison]
If the question itself is mis-framed, reframe it and answer the better version.
150-300 words.
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: (premises the dominant option depends on)
what_would_change_my_mind: (what would make the user's original options correct)
unknowns: (facts that would change the upside/effort scoring)---
Outsider
You are the Outsider on an LLM Council. You have zero context on this user's industry, company, or prior decisions — and that is your advantage. Your role: react as a smart person from a completely unrelated field would.
You must name the specific field you are channelling (e.g., "I'm reading this as an emergency room triage nurse" or "I'm reading this as a structural engineer"). Pick the field whose decision-making methodology is most instructively different from the user's apparent domain.
Ask: why is this even a decision? Why those options and not others? What parts of the question only make sense inside a bubble? What would your chosen field's training say about this framing?
The question:
{{FRAMED_QUESTION}}
Structure:
FIELD: [the field you're channelling and why]
NAIVE READ: [what this question looks like without domain context]
BUBBLE SPOTS: [jargon or insider logic doing unearned work]
CROSS-DOMAIN INSIGHT: [what your field's methodology would prescribe]
150-300 words. Curious, not sarcastic. If the question would sound insane to your chosen field, say exactly why.
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: (what you're assuming about the user's domain)
what_would_change_my_mind: (domain context that would make the framing sensible)
unknowns: (what you'd need to know to give a sharper outsider read)---
Executor
You are the Executor on an LLM Council. The other advisors are dealing with the question at an abstract level. Your role: ignore that entirely. Focus on what actually happens, what it costs, and what breaks.
You must produce structured output using two frameworks:
1. OODA STAGE: Name which loop phase the user is in (Observe / Orient / Decide / Act) and whether they're stuck in the right phase for this question.
2. RICE SCORING for each option the user named (and any obvious options they missed):
- Reach: how many people/systems/decisions does this affect? (number or scale: S/M/L/XL)
- Impact: what's the magnitude of effect per unit? (H/M/L)
- Confidence: how certain are these estimates? (%)
- Effort: person-weeks or equivalent cost
- RICE Score: (Reach × Impact × Confidence) / Effort — higher is better
3. DATA COMPLETENESS CHECK: List the 3 most critical inputs your plan depends on. If any are unknown, mark:
STATUS: DRAFT — BLOCKED ON <input name>
The question:
{{FRAMED_QUESTION}}
150-300 words max for narrative sections. RICE table is additional.
=== CONFIDENCE ===
confidence: high | medium | low
assumptions: (what the RICE scores assume)
what_would_change_my_mind: (data that would flip the RICE ranking)
unknowns: (the 3 most critical missing inputs — same as Data Completeness Check)#!/usr/bin/env bash
# Append a council run record to the JSONL journal atomically.
# Usage: journal_append.sh '<json_string>'
# The JSON string is passed as the first argument.
set -euo pipefail
JOURNAL_DIR="$(cd "$(dirname "$0")/../journal" 2>/dev/null && pwd || echo "$(dirname "$0")/../journal")"
JOURNAL_FILE="$JOURNAL_DIR/council-log.jsonl"
LOCK_DIR="$JOURNAL_DIR/.append.lock.d"
mkdir -p "$JOURNAL_DIR"
[ -f "$JOURNAL_FILE" ] || : > "$JOURNAL_FILE"
if [ "${1:-}" = "" ]; then
echo "Error: no JSON payload provided" >&2
exit 1
fi
JSON_PAYLOAD="$1"
# jq is required — silent skip lets malformed payloads corrupt the journal.
if ! command -v jq >/dev/null 2>&1; then
echo "Error: jq is required. Install with: brew install jq" >&2
exit 2
fi
# Validate JSON (structural) before taking the lock.
if ! printf '%s' "$JSON_PAYLOAD" | jq -e . >/dev/null 2>&1; then
echo "Error: invalid JSON payload" >&2
exit 3
fi
# Normalise to single line; strip embedded newlines that would break JSONL.
JSON_LINE="$(printf '%s' "$JSON_PAYLOAD" | jq -c .)"
# Portable append lock: prefer flock (Linux / Homebrew util-linux); fall back
# to mkdir-based locking, which is atomic across POSIX filesystems incl. macOS.
acquire_lock() {
if command -v flock >/dev/null 2>&1; then
exec 200>"$JOURNAL_FILE.lock"
flock -x 200
LOCK_MODE="flock"
return 0
fi
# mkdir is atomic; spin with bounded retries (~5s total).
local tries=0
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
tries=$((tries + 1))
if [ "$tries" -gt 50 ]; then
echo "Error: could not acquire journal lock after ~5s ($LOCK_DIR)" >&2
exit 4
fi
sleep 0.1
done
LOCK_MODE="mkdir"
}
release_lock() {
if [ "${LOCK_MODE:-}" = "mkdir" ]; then
rmdir "$LOCK_DIR" 2>/dev/null || true
fi
# flock mode: fd 200 released automatically on exit.
}
LOCK_MODE=""
trap 'release_lock' EXIT INT TERM
acquire_lock
printf '%s\n' "$JSON_LINE" >> "$JOURNAL_FILE"
release_lock
trap - EXIT INT TERM
echo "Journal updated: $JOURNAL_FILE"
#!/usr/bin/env bash
# Record the outcome of a prior council run by sha1-prefix match.
# Usage: journal_outcome.sh <sha1_prefix> <outcome_note...>
#
# Looks up runs in council-log.jsonl whose question_sha1_prefix starts with
# <sha1_prefix>, then sets their .outcome field to <outcome_note>. Uses the
# same mkdir-lock as journal_append.sh so concurrent writes can't interleave.
set -euo pipefail
JOURNAL_DIR="$(cd "$(dirname "$0")/../journal" 2>/dev/null && pwd || echo "$(dirname "$0")/../journal")"
JOURNAL_FILE="$JOURNAL_DIR/council-log.jsonl"
LOCK_DIR="$JOURNAL_DIR/.append.lock.d"
if [ "${1:-}" = "" ] || [ "${2:-}" = "" ]; then
echo "Usage: journal_outcome.sh <sha1_prefix> <outcome note ...>" >&2
exit 1
fi
PREFIX="$1"; shift
NOTE="$*"
if ! command -v jq >/dev/null 2>&1; then
echo "Error: jq is required. Install with: brew install jq" >&2
exit 2
fi
if [ ! -f "$JOURNAL_FILE" ]; then
echo "Error: no journal found at $JOURNAL_FILE" >&2
exit 3
fi
acquire_lock() {
if command -v flock >/dev/null 2>&1; then
exec 200>"$JOURNAL_FILE.lock"
flock -x 200
LOCK_MODE="flock"
return 0
fi
local tries=0
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
tries=$((tries + 1))
[ "$tries" -gt 50 ] && { echo "Error: lock timeout" >&2; exit 4; }
sleep 0.1
done
LOCK_MODE="mkdir"
}
release_lock() {
[ "${LOCK_MODE:-}" = "mkdir" ] && rmdir "$LOCK_DIR" 2>/dev/null || true
}
LOCK_MODE=""
trap 'release_lock' EXIT INT TERM
acquire_lock
TMP="$(mktemp "${JOURNAL_FILE}.XXXXXX")"
MATCHED=0
while IFS= read -r line; do
[ -z "$line" ] && continue
if ! printf '%s' "$line" | jq -e . >/dev/null 2>&1; then
# Preserve corrupt lines untouched.
printf '%s\n' "$line" >> "$TMP"
continue
fi
CUR_PREFIX="$(printf '%s' "$line" | jq -r '.question_sha1_prefix // ""')"
case "$CUR_PREFIX" in
"$PREFIX"*)
printf '%s' "$line" | jq -c --arg n "$NOTE" '.outcome = $n' >> "$TMP"
MATCHED=$((MATCHED + 1))
;;
*)
printf '%s\n' "$line" >> "$TMP"
;;
esac
done < "$JOURNAL_FILE"
if [ "$MATCHED" -eq 0 ]; then
rm -f "$TMP"
release_lock
trap - EXIT INT TERM
echo "No matching run for sha1 prefix: $PREFIX" >&2
exit 5
fi
mv "$TMP" "$JOURNAL_FILE"
release_lock
trap - EXIT INT TERM
echo "Updated $MATCHED run(s) with outcome: $NOTE"
#!/usr/bin/env bash
# Search the council journal for runs similar to a given question.
# Usage: journal_search.sh "<raw question text>"
#
# Matches first on sha1-prefix (stable key), then falls back to keyword
# overlap against recommendation_one_liner + dissent_ledger text. Caps at
# 2 results per SKILL.md Step 2.
set -euo pipefail
JOURNAL_DIR="$(cd "$(dirname "$0")/../journal" 2>/dev/null && pwd || echo "$(dirname "$0")/../journal")"
JOURNAL_FILE="$JOURNAL_DIR/council-log.jsonl"
MAX_RESULTS=2
[ ! -f "$JOURNAL_FILE" ] && exit 0
[ ! -s "$JOURNAL_FILE" ] && exit 0
QUERY="${1:-}"
[ -z "$QUERY" ] && exit 0
if ! command -v jq >/dev/null 2>&1; then
echo "Warning: jq not found — journal search skipped" >&2
exit 0
fi
# Compute the query sha (same recipe as SKILL.md Step 9).
QUERY_SHA="$(printf '%s' "$QUERY" | shasum | cut -c1-8)"
# Pass 1: exact sha1-prefix match.
SHA_HITS=$(jq -rc --arg s "$QUERY_SHA" \
'select(.question_sha1_prefix != null and (.question_sha1_prefix | startswith($s[0:8])))' \
"$JOURNAL_FILE" 2>/dev/null | head -n "$MAX_RESULTS" || true)
format_line() {
jq -r '"[\(.ts // "unknown date")] sha1:\(.question_sha1_prefix // "?") mode:\(.mode // "?") — \(.recommendation_one_liner // "(no summary)") — outcome: \(.outcome // "not recorded")"'
}
if [ -n "$SHA_HITS" ]; then
printf '%s\n' "$SHA_HITS" | while IFS= read -r line; do
[ -z "$line" ] && continue
printf '%s' "$line" | format_line
done
exit 0
fi
# Pass 2: keyword overlap. Keywords are lowercased, length >= 4, deduped,
# and grep'd with -F (fixed-string) to avoid regex injection from user input.
KEYWORDS=$(printf '%s' "$QUERY" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z' '\n' \
| awk 'length >= 4' | sort -u | head -20)
[ -z "$KEYWORDS" ] && exit 0
RESULTS=0
while IFS= read -r line; do
[ -z "$line" ] && continue
if ! printf '%s' "$line" | jq -e . >/dev/null 2>&1; then continue; fi
# Parenthesise // to force correct precedence — // binds looser than +, so
# without parens the ledger join is never reached when rec is non-null.
REC_TEXT=$(printf '%s' "$line" | jq -r \
'(.recommendation_one_liner // "") + " " + ((.dissent_ledger // []) | join(" "))' \
| tr '[:upper:]' '[:lower:]')
SCORE=0
for kw in $KEYWORDS; do
if printf '%s' "$REC_TEXT" | grep -qF -- "$kw"; then
SCORE=$((SCORE + 1))
fi
done
if [ "$SCORE" -ge 2 ]; then
printf '%s' "$line" | format_line
RESULTS=$((RESULTS + 1))
[ "$RESULTS" -ge "$MAX_RESULTS" ] && break
fi
done < "$JOURNAL_FILE"
exit 0
#!/usr/bin/env bash
# Aggregate the council journal to surface patterns for persona refinement.
# Usage: meta_analysis.sh
# Outputs a dated amendment proposal to journal/persona-diffs/YYYY-MM-DD-amendments.md
# Requires jq for JSON parsing.
SCRIPT_DIR="$(dirname "$0")"
JOURNAL_DIR="$SCRIPT_DIR/../journal"
JOURNAL_FILE="$JOURNAL_DIR/council-log.jsonl"
DIFFS_DIR="$JOURNAL_DIR/persona-diffs"
TODAY=$(date +%Y-%m-%d)
OUTPUT_FILE="$DIFFS_DIR/$TODAY-amendments.md"
if [ ! -f "$JOURNAL_FILE" ]; then
echo "No journal found at $JOURNAL_FILE. Run at least one council first." >&2
exit 1
fi
if ! command -v jq &>/dev/null; then
echo "jq is required for meta-analysis. Install via: brew install jq" >&2
exit 1
fi
mkdir -p "$DIFFS_DIR"
TOTAL=$(wc -l < "$JOURNAL_FILE" | tr -d ' ')
if [ "$TOTAL" -lt 5 ]; then
echo "Only $TOTAL runs in journal — need at least 5 for meaningful patterns." >&2
echo "Run more councils and record outcomes before running meta-analysis." >&2
exit 1
fi
echo "Analyzing $TOTAL council runs..."
# Count outcomes — use boolean select; `jq -r` on `null` emits nothing,
# so the prior `select(.outcome == null) | .outcome` pattern always produced 0.
OUTCOMES_RECORDED=$(jq -r 'select((.outcome // null) != null) | "x"' "$JOURNAL_FILE" | wc -l | tr -d ' ')
OUTCOMES_NULL=$(jq -r 'select((.outcome // null) == null) | "x"' "$JOURNAL_FILE" | wc -l | tr -d ' ')
# Confidence distribution per persona — aggregate all 5, not just 2.
count_conf() { # $1=persona key, $2=level
jq -r ".advisors_confidence.$1 // empty" "$JOURNAL_FILE" | grep -c "^$2$" || true
}
RED_TEAM_HIGH=$(count_conf red_team high); RED_TEAM_LOW=$(count_conf red_team low)
FIRST_PRIN_HIGH=$(count_conf first_principles high); FIRST_PRIN_LOW=$(count_conf first_principles low)
EXPAND_HIGH=$(count_conf expansionist high); EXPAND_LOW=$(count_conf expansionist low)
OUTSIDER_HIGH=$(count_conf outsider high); OUTSIDER_LOW=$(count_conf outsider low)
EXEC_HIGH=$(count_conf executor high); EXEC_LOW=$(count_conf executor low)
# Bias frequency
TOP_BIASES=$(jq -r '.biases_flagged[]? // empty' "$JOURNAL_FILE" | sort | uniq -c | sort -rn | head -5)
# Mode distribution
MODE_QUICK=$(jq -r 'select(.mode == "Quick") | .mode' "$JOURNAL_FILE" | wc -l | tr -d ' ')
MODE_STANDARD=$(jq -r 'select(.mode == "Standard") | .mode' "$JOURNAL_FILE" | wc -l | tr -d ' ')
MODE_DEEP=$(jq -r 'select(.mode == "Deep") | .mode' "$JOURNAL_FILE" | wc -l | tr -d ' ')
# Chairman confidence distribution
CHAIR_HIGH=$(jq -r 'select(.chairman_confidence == "high") | .chairman_confidence' "$JOURNAL_FILE" | wc -l | tr -d ' ')
CHAIR_LOW=$(jq -r 'select(.chairman_confidence == "low") | .chairman_confidence' "$JOURNAL_FILE" | wc -l | tr -d ' ')
# Write amendment proposal
cat > "$OUTPUT_FILE" << EOF
# Council Meta-Analysis — $TODAY
Generated from $TOTAL council runs.
## Summary Statistics
| Metric | Value |
|---|---|
| Total runs | $TOTAL |
| Outcomes recorded | $OUTCOMES_RECORDED |
| Outcomes missing | $OUTCOMES_NULL |
| Quick mode runs | $MODE_QUICK |
| Standard mode runs | $MODE_STANDARD |
| Deep mode runs | $MODE_DEEP |
| Chairman high-confidence runs | $CHAIR_HIGH |
| Chairman low-confidence runs | $CHAIR_LOW |
## Bias Frequency (top flags)
\`\`\`
$TOP_BIASES
\`\`\`
## Persona Confidence Patterns
| Persona | High | Low |
|---|---|---|
| Red Team | $RED_TEAM_HIGH | $RED_TEAM_LOW |
| First Principles | $FIRST_PRIN_HIGH | $FIRST_PRIN_LOW |
| Expansionist | $EXPAND_HIGH | $EXPAND_LOW |
| Outsider | $OUTSIDER_HIGH | $OUTSIDER_LOW |
| Executor | $EXEC_HIGH | $EXEC_LOW |
## Proposed Amendments
Review these manually. Accept, reject, or modify as appropriate. Never auto-apply.
EOF
# Generate contextual amendment suggestions based on patterns
if [ "$CHAIR_LOW" -gt "$((TOTAL / 3))" ]; then
echo "### Chairman confidence is low in >33% of runs" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Possible causes: questions are routinely under-constrained before council runs; bias audit flags are not being heeded; advisor confidence blocks are systematically uncertain." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "**Proposed:** Add a more aggressive pre-triage check — if framed question lacks explicit stakes and at least 2 concrete options, always ask for clarification before fan-out." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$RED_TEAM_LOW" -gt "$RED_TEAM_HIGH" ]; then
echo "### Red Team confidence skews low more than high" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Red Team is being uncertain when it should be attacking confidently. The pre-mortem structure may need strengthening — agents that hedge on the attack are not fulfilling the adversarial role." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "**Proposed:** Add to Red Team prompt: 'If you cannot identify a specific failure mode, force yourself to construct the most plausible one. A speculative attack is more useful than a hedged non-attack.'" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$EXEC_LOW" -gt "$EXEC_HIGH" ]; then
echo "### Executor confidence is frequently low" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Executor is correctly triggering DATA COMPLETENESS: DRAFT STATUS when inputs are missing. This may indicate most council questions are under-specified for operational planning. Consider adding a step that prompts the user for key operational facts before running the Executor." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$FIRST_PRIN_LOW" -gt "$FIRST_PRIN_HIGH" ]; then
echo "### First Principles confidence skews low" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "First Principles is hedging on reframings. The Tree-of-Thoughts approach may need tighter constraints — force commitment to the strongest reframing rather than presenting all three as equal." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$EXPAND_LOW" -gt "$EXPAND_HIGH" ]; then
echo "### Expansionist confidence skews low" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Expansionist is unsure of its alternative options. This may indicate the questions are too domain-specific for cross-frame ideation. Consider adding: 'If you cannot generate 3 genuinely novel options, generate 2 and explain why the frame constrains further options.'" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$OUTSIDER_LOW" -gt "$OUTSIDER_HIGH" ]; then
echo "### Outsider confidence skews low" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Outsider is frequently uncertain, which may be appropriate (the role is deliberately under-informed). If Outsider confidence is *always* low, the cross-domain insight may not be adding value — consider whether the field-channelling approach needs sharper selection criteria." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
if [ "$OUTCOMES_NULL" -gt "$((TOTAL * 2 / 3))" ]; then
echo "### Outcome tracking is sparse ($OUTCOMES_NULL/$TOTAL runs have no outcome recorded)" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "Without outcomes, meta-analysis cannot improve persona calibration over time. The self-improvement loop depends on knowing which recommendations played out. Remind users more prominently to record outcomes." >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "**Proposed:** Add to the end-of-run user message: 'Recording outcomes is what makes the council improve over time. Even a one-word note (\"worked\", \"wrong\", \"still deciding\") helps.'" >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
fi
echo "" >> "$OUTPUT_FILE"
echo "---" >> "$OUTPUT_FILE"
echo "_This file was auto-generated. Apply amendments manually to \`references/personas.md\` after review._" >> "$OUTPUT_FILE"
echo "Amendment proposal written to: $OUTPUT_FILE"
cat "$OUTPUT_FILE"