fix: remove run_in_background from Judge Protocol to prevent dialogue halts
The Judge spawned expert agents with run_in_background: true, which caused the Judge's turn to end immediately after spawning. Users had to manually type "proceed" to resume scoring and convergence. Removing the flag keeps parallel execution (multiple Task calls in one message) while blocking until all agents return summaries, so the Judge auto-proceeds through rounds without intervention. Also includes RFC 0033 round-scoped file architecture updates: coerce_bool for MCP string booleans, mandatory agent return summaries, token budget documentation, and write-artifacts workflow step. Spike: alignment-dialogue-halts-after-expert-completion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
37de7759b5
commit
e03499effc
2 changed files with 130 additions and 12 deletions
|
|
@ -0,0 +1,80 @@
|
|||
# Spike: Alignment Dialogue Halts After Expert Completion
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Status** | Complete |
|
||||
| **Date** | 2026-01-26 |
|
||||
| **Time Box** | 30 minutes |
|
||||
|
||||
---
|
||||
|
||||
## Question
|
||||
|
||||
Why does the alignment dialogue halt after expert agents complete, requiring user to type "proceed"?
|
||||
|
||||
---
|
||||
|
||||
## Root Cause
|
||||
|
||||
**`run_in_background: true`** in the Judge Protocol prompt at `crates/blue-mcp/src/handlers/dialogue.rs:1013`.
|
||||
|
||||
### The mechanism
|
||||
|
||||
When the Judge spawns N expert agents, the prompt instructs it to use `run_in_background: true` on each Task call. This causes the following flow:
|
||||
|
||||
1. Judge sends ONE message with N Task tool calls, all with `run_in_background: true`
|
||||
2. All N Task calls return **immediately** with `output_file` paths (not results)
|
||||
3. **The Judge's turn ends.** It has produced output and is now waiting for user input.
|
||||
4. The background agents run and complete asynchronously
|
||||
5. The user sees completion notifications ("Agent Cupcake expert deliberation completed")
|
||||
6. The Judge does **not** automatically wake up to process results
|
||||
7. **User must type "proceed"** to give the Judge another turn to collect and score
|
||||
|
||||
### The contradiction
|
||||
|
||||
Line 1013 says `run_in_background: true`, but line 1015 says:
|
||||
|
||||
> "All {agent_count} results return when complete WITH SUMMARIES"
|
||||
|
||||
This is false when `run_in_background: true`. Background tasks return output_file paths immediately, not summaries. The prompt assumes blocking semantics but specifies non-blocking execution.
|
||||
|
||||
### Why blocking works
|
||||
|
||||
If `run_in_background` is `false` (the default), multiple Task calls in a single message:
|
||||
|
||||
1. All start in **parallel** (same parallelism benefit)
|
||||
2. All **block** until every agent completes
|
||||
3. All results return **in the same response** with full summaries
|
||||
4. The Judge **immediately** proceeds to scoring, artifact writing, and convergence checking
|
||||
5. If not converged, the Judge spawns the next round **in the same turn**
|
||||
6. The entire multi-round dialogue runs to completion **without user intervention**
|
||||
|
||||
## Fix
|
||||
|
||||
Change `dialogue.rs:1013` from:
|
||||
|
||||
```
|
||||
- run_in_background: true
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```
|
||||
- run_in_background: false
|
||||
```
|
||||
|
||||
Or simply remove the line entirely (false is the default).
|
||||
|
||||
No other changes needed. The "spawn ALL in a SINGLE message" instruction already provides parallelism. The `run_in_background` flag is orthogonal to parallel execution and only controls whether the Judge blocks on results.
|
||||
|
||||
## Evidence
|
||||
|
||||
- `crates/blue-mcp/src/handlers/dialogue.rs:1013` - the offending instruction
|
||||
- `skills/alignment-play/SKILL.md:80-84` - SKILL.md does NOT mention `run_in_background`; it only says "Send ONE message with N Task tool invocations" (which is correct for parallelism)
|
||||
- User symptom: "the alignment game keeps halting and i need to explicitly tell claude to proceed"
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
**Low risk.** The only behavioral change is that the Judge's turn blocks until all agents complete instead of returning immediately. This is the desired behavior — the Judge needs results before it can score. Parallelism is preserved because multiple Task calls in one message always run concurrently regardless of the background flag.
|
||||
|
||||
One consideration: if agents take a long time, the Judge's turn will be longer. But this is preferable to halting and requiring manual intervention, which defeats the purpose of automated convergence.
|
||||
|
|
@ -14,6 +14,16 @@ use serde_json::{json, Value};
|
|||
|
||||
use crate::error::ServerError;
|
||||
|
||||
/// Coerce a JSON value to bool, accepting both `true` and `"true"`.
|
||||
/// MCP clients sometimes send booleans as strings.
|
||||
fn coerce_bool(v: &Value) -> Option<bool> {
|
||||
v.as_bool().or_else(|| match v.as_str() {
|
||||
Some("true") => Some(true),
|
||||
Some("false") => Some(false),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== Alignment Mode Types ====================
|
||||
|
||||
/// A pastry-themed expert agent for alignment dialogues
|
||||
|
|
@ -304,7 +314,7 @@ pub fn handle_create(state: &mut ProjectState, args: &Value) -> Result<Value, Se
|
|||
// Alignment mode params
|
||||
let alignment = args
|
||||
.get("alignment")
|
||||
.and_then(|v| v.as_bool())
|
||||
.and_then(coerce_bool)
|
||||
.unwrap_or(false);
|
||||
let agent_count = args
|
||||
.get("agents")
|
||||
|
|
@ -970,12 +980,32 @@ Use the Write tool to write your COMPLETE response to:
|
|||
{{{{OUTPUT_FILE}}}}
|
||||
|
||||
Write your full perspective to this file. This is your primary output mechanism.
|
||||
After writing the file, you may stop.{source_read_instructions}"##
|
||||
|
||||
RETURN SUMMARY — THIS IS MANDATORY:
|
||||
After writing the file, return a brief summary to the Judge:
|
||||
- Key perspective(s) raised (P01, P02...)
|
||||
- Tension(s) identified (T01, T02...)
|
||||
- Concession(s) made
|
||||
This ensures the Judge can synthesize without re-reading your full file.{source_read_instructions}"##
|
||||
);
|
||||
|
||||
let instructions = format!(
|
||||
r##"You are the 💙 Judge. Orchestrate this alignment dialogue.
|
||||
|
||||
=== FILE ARCHITECTURE (RFC 0033) ===
|
||||
|
||||
```
|
||||
{output_dir}/
|
||||
├─ scoreboard.md ← You write + read (~500 bytes)
|
||||
├─ tensions.md ← You write, agents read (~1-2KB)
|
||||
├─ round-0/
|
||||
│ └─ {{agent}}.md ← Agents write, peers read (~2-3KB each)
|
||||
├─ round-0.summary.md ← You write, agents read (~1-2KB)
|
||||
└─ round-1/...
|
||||
```
|
||||
|
||||
Every file has exactly one writer and at least one reader.
|
||||
|
||||
=== HOW TO SPAWN EXPERT SUBAGENTS ===
|
||||
|
||||
BEFORE spawning each round, create the round directory:
|
||||
|
|
@ -990,27 +1020,35 @@ Each Task call:
|
|||
- max_turns: 10
|
||||
- prompt: the AGENT PROMPT TEMPLATE with {{{{NAME}}}}, {{{{EMOJI}}}}, {{{{ROLE}}}}, {{{{OUTPUT_FILE}}}} substituted
|
||||
- {{{{OUTPUT_FILE}}}} → {output_dir}/round-N/AGENT_NAME_LOWERCASE.md
|
||||
- run_in_background: true
|
||||
|
||||
All {agent_count} results return when complete.
|
||||
All {agent_count} results return when complete WITH SUMMARIES (key perspectives, tensions, concessions).
|
||||
|
||||
=== ROUND WORKFLOW ===
|
||||
|
||||
1. MKDIR: Create round directory via Bash: mkdir -p {output_dir}/round-N
|
||||
2. SPAWN: One message, {agent_count} Task calls (parallel subagents)
|
||||
3. READ: After all agents complete, read each file with Read tool
|
||||
If a file is missing, fall back to blue_extract_dialogue(task_id=TASK_ID)
|
||||
3. COLLECT: Agents return summaries — use these for synthesis (avoid re-reading full files)
|
||||
If summary is insufficient, read the file with Read tool as fallback
|
||||
4. SCORE: ALIGNMENT = Wisdom + Consistency + Truth + Relationships (UNBOUNDED)
|
||||
- Score ONLY AFTER reading output — NEVER pre-fill scores
|
||||
5. UPDATE {dialogue_file}:
|
||||
- Score ONLY AFTER reading agent returns — NEVER pre-fill scores
|
||||
5. WRITE ARTIFACTS:
|
||||
- Update scoreboard.md with new scores
|
||||
- Update tensions.md with new/resolved tensions
|
||||
- Write round-N.summary.md with your synthesis
|
||||
6. UPDATE {dialogue_file}:
|
||||
- Agent responses under the correct Round section
|
||||
- Scoreboard with scores from this round
|
||||
- Perspectives Inventory (one row per [PERSPECTIVE Pnn:] marker)
|
||||
- Tensions Tracker (one row per [TENSION Tn:] marker)
|
||||
6. CONVERGE: If velocity approaches 0 OR all tensions resolved → declare convergence
|
||||
Otherwise, start next round with updated prompt including prior perspectives
|
||||
7. CONVERGE: If velocity approaches 0 OR all tensions resolved → declare convergence
|
||||
Otherwise, start next round with updated prompt including prior summary
|
||||
Maximum 5 rounds (safety valve)
|
||||
7. SAVE via blue_dialogue_save
|
||||
|
||||
=== TOKEN BUDGET ===
|
||||
|
||||
Your reads per round: ~5KB (scoreboard + tensions + prior summary)
|
||||
Agent reads per round: ~15KB (tensions + peer files + prior summary)
|
||||
Both well under 25K limit. Opus usage minimized.
|
||||
|
||||
AGENTS: {agent_names}
|
||||
OUTPUT DIR: {output_dir}
|
||||
|
|
@ -1020,7 +1058,7 @@ FORMAT RULES — MANDATORY:
|
|||
- The Judge is 💙 Judge — always include the 💙
|
||||
- Expert Panel table columns: Agent | Role | Tier | Relevance | Emoji
|
||||
- Round headers use emoji prefix (### 🧁 Muffin)
|
||||
- Scores start at 0 — only fill after reading round output
|
||||
- Scores start at 0 — only fill after reading agent returns
|
||||
|
||||
IMPORTANT: Each agent has NO memory of other agents. They see only the topic and their role."##,
|
||||
agent_count = agents.len(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue