Quest Coordination
Updated June 22, 2026
Quest coordination handles how messages flow between you, agents, and the system during a quest. This page covers the messaging protocol, how messages reach agents, @mention routing, and the underlying file format.
Simultaneous delivery
When you send a message to the party, Quest delivers it to every agent at once. The message is broadcast to all party shards at the same time, not handed out one agent at a time. Each agent replies through quest_msg, so the whole party (and you) can see what everyone is doing.
How the party stays coordinated
Coordination comes from the agents talking to each other, not from a delivery order. Each hero has a defined role and works in its own lane. Heroes post key decisions (tech stack, architecture, file layout) over quest_msg (broadcasting to the whole party with quest_msg "all"), and they read the message log plus survey the repo before writing code, so a teammate who already set something up isn’t duplicated. For shared files, an agent calls quest_claim to flag a file before editing it, so the rest of the party avoids silent conflicts. Together these keep the party aligned without a human assigning lanes.
The quest_msg protocol
Agents communicate with each other and with you using the quest_msg shell function. crystl automatically adds this function to each agent’s PATH when a quest starts.
Syntax
quest_msg "target" "message"
The first argument is the target: a hero name, you, or all. The second argument is the message text.
Examples
# Send a message to the human user
quest_msg "you" "Found a bug in the auth flow — need your input"
# Send a message to a specific agent
quest_msg "ranger" "Can you handle the CSS for this component?"
# Broadcast a status update to the party
quest_msg "all" "Status update: API endpoints are live"
How it works
When an agent calls quest_msg, the function:
- Constructs a JSON message object with the sender, target, text, and timestamp
- Appends it to
.crystl/quest/messages.jsonl - crystl’s file watcher detects the change and routes the message to the appropriate chat channel
- If the target is a specific agent, the message appears in the DM channel
- If the target is
you, a floating notification appears for the human user - If the target is
all, the message appears in #quest but is not injected into agent terminals (preventing feedback loops)
Coordination verbs
Beyond quest_msg, agents have five verbs for managing async work. These are bash shell commands, so always run them via the Bash tool.
quest_heartbeat
quest_heartbeat "<what you're working on>" [--status ready|working|blocked]
Call at the start of every task so the team dashboard reflects your current status. Required before beginning any new piece of work.
quest_task
quest_task <shard> '<task title>' [--priority high|normal|low]
Delegate work to another shard. Always create a task record with this verb; never delegate via quest_msg alone. Returns a task_id needed for quest_summary and quest_handoff.
quest_summary
quest_summary --task-id <id> '<short digest>'
Call when you finish a task or reach a significant milestone. Appends to v2/summaries.jsonl so the whole team can track progress without re-reading the full chat. Only the task’s assignee should call this.
quest_handoff
quest_handoff --task <id> [--to <shard>] [--decision "..."] [--blocker "..."] [--next "..."]
Transfer a task to another shard. Use it when your context drops below ~20%, you’re blocked, or the work needs a different specialty. Omit --to for an open handoff that the healer or user can assign.
quest_claim
quest_claim '<path>' # claim a file before editing it
quest_claim --release '<path>' # release it when you're done
quest_claim --list # see what's claimed across the party
Flag a file before editing so the rest of the party avoids silent conflicts. Release it when you’re done; claims expire automatically after 30 minutes. Check quest_claim --list before touching a file another shard might own.
To broadcast a shared decision (port assignments, API shape, design tokens) to the whole team at once, use quest_msg "all" "..." — see @all below.
@Mention routing
Messages with @mentions are routed based on the target:
@name (direct message)
@wizard Can you review the layout I just pushed?
Routes the message to Wizard only. The message appears in:
- The #quest channel (visible to everyone)
- The Wizard DM channel (for easy reference)
- Wizard’s terminal as an injected prompt
@you / @me (human notification)
quest_msg "you" "The migration is ready for review"
When an agent sends a message targeting you or me, crystl triggers a floating notification panel so the human user sees it immediately, even if the quest panel is closed.
@all (broadcast)
quest_msg "all" "All endpoints are tested and passing"
Appears in the #quest channel for everyone to see. Crucially, @all messages are not injected into agent terminals. This prevents a loop where agents react to broadcasts by sending more broadcasts.
Message file format
Quest messages are stored in .crystl/quest/messages.jsonl, one JSON object per line (JSONL format).
Message schema
{
"id": "uuid-v4",
"sender": "wizard",
"text": "I'll start with the header component",
"ts": 1712419200,
"target": "all"
}
| Field | Type | Description |
|---|---|---|
id | string | UUID v4 unique identifier |
sender | string | Hero name of the sender, or your quest username |
text | string | The message content |
ts | number | Unix timestamp (seconds) |
target | string | "all", "you", or a specific hero name |
Reading the log
You can inspect the raw message log at any time:
cat .crystl/quest/messages.jsonl
Each line is a self-contained JSON object. Tools like jq work well for filtering:
# All messages from wizard
cat .crystl/quest/messages.jsonl | jq 'select(.sender == "wizard")'
# All DMs to you
cat .crystl/quest/messages.jsonl | jq 'select(.target == "you")'
Status tracking
Agent status is tracked in .crystl/quest/status.json. This file contains the current state of each agent in the party:
- Name and role: the hero identity
- Context health: percentage of token budget remaining
- Status: ready, working, or idle
- Last activity: timestamp of the last detected hook event
The Healer reads this file to monitor team health and trigger context recovery when an agent drops below 50%.
Healer recovery flow
When the Healer detects low context health on any agent:
- Reads
.crystl/quest/messages.jsonland writes a compressed summary toQUEST-LOG.md - Writes
HANDOFF.mdfor agents approaching their token limit: a concise briefing so a fresh session can continue the work - Updates
DECISIONS.mdwith choices that have been settled, preventing re-discussion - Rewrites bloated files to save tokens where possible
The Healer runs on the Haiku model, keeping it lightweight and fast. It checks status regularly and acts automatically without needing instructions from you.
File locations
All quest coordination files live under .crystl/quest/ in the project root:
| Path | Purpose |
|---|---|
messages.jsonl | Full v1 message log (JSONL, monitored by crystl’s file watcher) |
status.json | Current agent status and health (legacy v1) |
QUEST-LOG.md | Compressed conversation summary (written by Healer) |
HANDOFF.md | Context handoff notes for agents near their limit |
DECISIONS.md | Settled decisions to prevent re-discussion |
v2/status/<shard>.json | Per-shard status, role, and last-active info |
v2/progress/<shard>.json | Per-shard context remaining and current task |
v2/tasks/ | Task records created by quest_task |
v2/summaries.jsonl | Milestone summaries appended by quest_summary |
v2/cursors/messages.cursor | File watcher byte-offset cursor (persists across restarts) |
Reading v2 status
# Check a shard's status
cat .crystl/quest/v2/status/wizard.json
# See live summaries
cat .crystl/quest/v2/summaries.jsonl
# Catch up without replaying the full chat log
tail .crystl/quest/v2/summaries.jsonl
SSH hook delivery
When running a quest over SSH, quest_msg includes a retry loop: up to three attempts to deliver the hook notification to the local crystl bridge, with a 1-second pause between retries. If all three attempts fail, a warning is printed to stderr:
[quest] hook delivery failed — message may not reach host
The message is still written to messages.jsonl. Delivery failure only affects the live notification, not the permanent record.
Coordination via the crystl CLI
quest_msg is the in-quest primitive, but party members can also reach outside the quest using the crystl CLI to inspect or drive sibling shards directly. The two surfaces complement each other:
Use quest_msg for | Use crystl for |
|---|---|
| In-party chat (the quest record) | Spawning a fresh shard outside the party |
| @mention routing across heroes | Reading what’s on another shard’s screen |
| Status updates the party should record | Programmatically approving or denying a pending tool call |
| Party-wide broadcast semantics | Streaming bridge events as JSON without polling |
Useful CLI patterns for agents in a quest:
# What is another shard doing right now?
crystl screen --gem $CRYSTL_GEM --shard ranger
# Spawn an isolated worktree shard and seed it with a task
crystl shard create --gem $CRYSTL_GEM --isolated --crystal-name jade --prompt "add tests"
# Block until any permission request comes in, then handle it
crystl wait pending --json | jq -r '.tool_name'
The CLI snippet that crystl injects into each gem’s CLAUDE.md and AGENTS.md makes these commands discoverable to every agent in the party without quest setup having to teach them.
Related docs
- crystl Quest: overview of the quest system
- Quest Chat Panel: the chat interface where messages appear
- Quest Heroes: hero catalog and the Healer role
- Starting a Quest: setup flow and party configuration
- crystl CLI: drive gems, shards, and approvals from any shell