Message Queue Composition in Munder Difflin: When Agents Can Type into Terminals
Munder Difflin uses a single per-agent MD queue stored in Zustand to hold pending messages, and agents can only type into terminals when five conditions are met: idle status, no auto-pause (or manual release), completed boot grace, terminal automation safety, and minimum 4.5-second spacing between deliveries.
Understanding the message queue composition in Munder Difflin is essential for anyone building automation harnesses around Claude Code. The repository implements a strict, policy-driven gating system that prevents AI agents from interrupting users or corrupting in-flight terminal sessions. This article breaks down the queue architecture, the safety gates, and the precise conditions that allow an agent to write to a PTY.
Two Queues: What Munder Difflin Controls vs. Claude Code
Munder Difflin manages only one of two queues involved in message delivery. Understanding the boundary prevents architectural confusion when debugging delayed messages.
| Queue | Location | Purpose |
|---|---|---|
| MD queue | Harness Zustand store (per-agent) | Holds messages Munder Difflin has accepted and parked until the terminal is free |
| Claude queue | Inside Claude Code itself | Holds text Claude Code has accepted but not yet started on—the harness never sees this |
The rest of this article focuses exclusively on the MD queue that Munder Difflin owns and operates.
MD Queue Composition and Storage
The MD queue lives in the harness's Zustand store at src/renderer/src/store/store.ts. It is a simple array per agent ID:
- Enqueue point:
enqueueMessage(agentId, text)—called by composers, Slack ingress, scheduled jobs, and inbox nudges - Dequeue point: The drain loop (effect #4 in
src/renderer/src/hooks/useHive.ts)—the only code that ever writes to a live terminal
This single-writer design eliminates race conditions between multiple automation sources.
The Five Conditions for Terminal Access
The drain loop evaluates these conditions on every store change (debounced 200 ms) plus a 3-second back-stop tick. All must be true for delivery:
| Condition | Implementation | Rationale |
|---|---|---|
Agent status is idle |
agent.status === 'idle' in store |
Prevents interrupting a running turn or active tool execution |
| Auto-delivery not paused or message manually released | !autoPause || msg.manual |
Supports floor-wide pausing via Command Center with per-message override |
| Past boot grace window | pastBootGrace flag |
Allows CLI startup banners to complete before automation begins |
isTerminalAutomationSafe(ptyId) returns true |
terminalPool.ts |
Verifies user still owns the prompt (see next section) |
| ≥ 4.5 s since last delivery | timeSinceLast > 4500 |
Prevents rapid writes that would jam TUI-based interfaces |
When a pause is active, queued rows display a Send now link that sets manual: true, bypassing only the pause check while preserving all other gates.
The "User Owns the Prompt" Safety Gate
isTerminalAutomationSafe in src/renderer/src/components/terminalPool.ts blocks delivery when any protection flag is active:
| Flag | Set When | Cleared When |
|---|---|---|
exited |
PTY process died | PTY respawns |
picker |
/model-style command opened a picker menu |
Enter, Escape, Ctrl-C typed into terminal, or 30-minute expiry (STALE_PICKER_MS) |
draft |
Unsubmitted text detected on prompt line | Submitting, clearing, or 30-minute expiry (STALE_INPUT_MS) |
settling |
Short repaint window after line was freed | Time elapsed |
Critical behavior: Expiration of picker or draft does not erase user text or close open menus. The queued message types after whatever already exists on the line.
Reading the prompt line uses actual xterm buffer inspection (term.buffer.active in terminalPool.ts) rather than keystroke counting—ensuring the system never inadvertently erases drafts or closes pickers.
The Drain Loop: High-Level Flow
composer / Slack ingress ──► enqueueMessage(agentId, text) ──► MD queue ──► drain (effect #4) ──► PTY
scheduled /compact (effect #6) ──► …
Key implementation files:
src/renderer/src/hooks/useHive.ts— inbox nudge (effect #3), drain loop (effect #4), scheduled/compact(effect #6)src/renderer/src/components/terminalPool.ts—isTerminalAutomationSafe, draft detection via buffer readingsrc/renderer/src/components/terminalAutomation.ts— policy logic for blocking and expiry timingsrc/renderer/src/store/store.ts— Zustand store containing per-agent MD queuesdocs/message-queue.md— full contract specification
Code Examples
Enqueueing a message from any producer:
// From composer, Slack ingress, or scheduled job
import { enqueueMessage } from 'src/renderer/src/store/store';
enqueueMessage('agent-42', 'npm install lodash');
The drain loop's safety check (from useHive.ts, effect #4):
if (
agent.status === 'idle' &&
!autoPause &&
pastBootGrace &&
isTerminalAutomationSafe(ptyId) &&
timeSinceLast > 4500
) {
const msg = mdQueue[agentId].shift(); // take front message
pty.write(msg.text); // type the text
pty.write('\r'); // submit (Enter)
}
Summary
- Munder Difflin maintains one MD queue per agent in its Zustand store—Claude Code's internal queue is invisible to the harness
- The drain loop in
useHive.tsis the sole writer to PTYs - Terminal access requires five simultaneous conditions: idle status, no pause (or manual release), completed boot grace,
isTerminalAutomationSafe, and 4.5-second spacing - The "user owns the prompt" gate (
terminalPool.ts) protects against automation during pickers, drafts, exits, and settle windows with 30-minute stale timeouts - Buffer reading in
terminalPool.tsensures read-only inspection—user state is never destroyed
Frequently Asked Questions
What happens if I click "Send now" during an auto-pause?
The message receives manual: true and moves to the front of the queue, bypassing only the pause check. All other conditions—idle status, boot grace, terminal safety, and timing—must still pass before the agent types into the terminal.
Can Munder Difflin see what's in Claude Code's internal queue?
No. The Claude queue exists entirely inside Claude Code and is opaque to the harness. Latency you observe may originate in either queue; only the MD queue is observable and controllable via Munder Difflin's UI and APIs.
Why does the system wait 4.5 seconds between deliveries?
The 4500 ms threshold prevents rapid back-to-back writes that would overwhelm TUI-based terminals (such as those running interactive CLI tools). This spacing ensures visual stability and prevents input jamming in complex terminal applications.
What clears a picker block besides the 30-minute timeout?
A picker flag clears when the user types Enter, Escape, or Ctrl-C into the terminal. These keystrokes signal intentional dismissal of the picker menu, at which point automation can safely resume.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →