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 command. crystl installs it (with the other verbs) into a per-shard bin dir and puts that dir on the agent’s PATH when a quest starts.

### Syntax

```
quest_msg "target" "message"
```

The first argument is the target: a hero name, your quest username, or `all`. The second argument is the message text.

### Examples

```
# Send a message to the human user
quest_msg "<your-username>" "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 command:

1. Constructs a JSON message object with the sender, target, text, and timestamp
2. Appends it to `.crystl/quest/messages.jsonl`
3. crystl’s file watcher detects the change and routes the message to the appropriate chat channel
4. If the target is a specific agent, the message appears in the DM channel and is injected into that agent’s terminal
5. If the target is your quest username, the message appears in #quest; an `@you` or `@me` in the body also pops a floating notification
6. If the target is `all`, the message appears in #quest and is delivered into every hero’s terminal exactly once (delivery never writes back to the bus, so no re-broadcast loop)

## 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>" [--blocker "<reason>"] [--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]
quest_task done <num|task_id>
```

Delegate work to another shard. Always create a task record; never delegate in prose alone. Returns a `task_id` plus a short `#num` (the chat-friendly handle), and stamps the quest level current at creation (reporting only; open tasks never gate a level advance). `quest_task done` completes a task by its `#num` or full id; it’s idempotent.

**Inline chat markers.** The fastest way to delegate is inside a normal `quest_msg`: a body line _starting_ with `@task:<hero> <what>` creates the task as you speak (no space after the colon), and a line starting with `@done:<N>` closes task #N. Markers must open their line; mid-line mentions are ignored, so quoting them is safe. Level advances list any carried-forward open tasks in #quest.

### quest_summary

```
quest_summary '<short digest>' [--task-id <id>]
```

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. Finishing a task delegated to you? Your summary closes it automatically; pass `--task-id` only when you own more than one open task. Only the task’s assignee may summarize against it.

### 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" "..."`.

## @Mention routing

Messages with @mentions are routed based on the target:

### @name (direct message)

```
@wizard Can you review the layout I just pushed?
```

Typed from #quest, this posts in the channel for everyone to read and is injected only into Wizard’s terminal. An agent’s `quest_msg "wizard" "..."` delivers the same way but lands in the wizard DM channel instead of #quest.

### @you / @me (human notification)

```
quest_msg "<your-username>" "@you the migration is ready for review"
```

Messages targeting your quest username reach you in the chat panel. When the body mentions `@you` or `@me` (or your username), crystl also triggers a floating notification card so you see 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 and is delivered into every hero’s terminal exactly once, excluding the sender. Delivery never writes back to the message bus, so a broadcast can’t trigger a loop of re-broadcasts. (An `@all` typed mid-body of an untargeted message still suppresses terminal delivery for the same reason.)

## 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"`, your quest username, 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 messages addressed to you (your quest username)
cat .crystl/quest/messages.jsonl | jq 'select(.target == "<your-username>")'
```

## 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](/content/docs/quest-heroes/index.html) 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:

1. Reads `.crystl/quest/messages.jsonl` and writes a compressed summary to `QUEST-LOG.md`
2. Writes `HANDOFF.md` for agents approaching their token limit: a concise briefing so a fresh session can continue the work
3. Updates `DECISIONS.md` with choices that have been settled, preventing re-discussion
4. Rewrites bloated files to save tokens where possible

The Healer runs at the **small** [model size](/content/docs/model-sizes/index.html), keeping it lightweight and fast. It checks status regularly and acts automatically without needing instructions from you.

## File locations

Quest coordination files are under `.crystl/quest/` in the project root, except the Healer’s docs, which are written to the project root itself:

| Path | Purpose |
| --- | --- |
| `.crystl/quest/messages.jsonl` | Full message log (JSONL, monitored by crystl’s file watcher) |
| `.crystl/quest/status.json` | Current agent status and health (legacy v1) |
| `.crystl/quest/v2/status/<shard>.json` | Per-shard status, role, and last-active info |
| `.crystl/quest/v2/progress/<shard>.json` | Per-shard context remaining and current task |
| `.crystl/quest/v2/tasks.json` | Task records created by `quest_task` |
| `.crystl/quest/v2/claims.json` | Advisory file claims from `quest_claim` |
| `.crystl/quest/v2/summaries.jsonl` | Milestone summaries appended by `quest_summary` |
| `.crystl/quest/v2/cursors/messages.cursor` | File watcher byte-offset cursor (persists across restarts) |
| `QUEST-LOG.md` | Compressed conversation summary (written by Healer, project root) |
| `HANDOFF.md` | Context handoff notes for agents near their limit (project root) |
| `DECISIONS.md` | Settled decisions to prevent re-discussion (project root) |

### 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](/content/docs/cli/index.html) 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'

# Dispatch work to a sibling shard and confirm it actually landed
crystl send --gem $CRYSTL_GEM --shard ranger "rerun the suite" --wait
```

Add `--wait` whenever a dispatch has to be _known_ to have arrived rather than assumed. The shard you’re sending to is usually mid-turn (that’s the normal case for a party member already working), and `--wait` counts that as delivered: it matches either a user turn in the target’s transcript or an entry in the agent’s own input queue. The exit code carries the outcome, and `3` in particular is the one to respect: **don’t re-send**, or the hero gets the same instruction twice.

| Exit | Meaning |
| --- | --- |
| `0` | Delivered. |
| `3` | Queued behind the target’s current turn. Don’t re-send. |
| `1` | No receipt from an idle shard. Nothing landed, so re-sending is safe. |

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](/content/docs/crystl-quest/index.html): overview of the quest system
- [quest chat panel](/content/docs/quest-chat/index.html): the chat interface where messages appear
- [quest heroes](/content/docs/quest-heroes/index.html): hero catalog and the Healer role
- [Starting a quest](/content/docs/starting-a-quest/index.html): setup flow and party configuration
- [crystl CLI](/content/docs/cli/index.html): drive gems, shards, and approvals from any shell
