Skip to content

Emoji Status Vocabulary and Format Specification

The #tasks Discord channel posts a message when each issue opens. Today that message is static text — operators cannot tell at a glance whether an issue is queued, in-flight, blocked, merged, or failed without clicking into the thread or GitHub.

This decision locks the emoji vocabulary and message format so that the renderer service (rc#437), Conductor wiring (rc#438), and rollout (rc#439) all share a single agreed schema. Nothing in this document is implementation guidance — it is the canonical spec.

See rc#433 for the full epic.


The leftmost emoji on the parent message encodes the current lifecycle state. One emoji per state; no compound primary.

StateEmojiRationale
queuedHourglass — waiting for dispatch
assigned👋Claimed but not started
in_progress🛠️Tools — Dev-E working
in_review👀Eyes — Review-E reading
changes_requested✏️Edit — back to Dev-E
ready_to_merge🎯Target — final gate
in_deploy🚀Rocket — Flux rolling (rc#413)
deployed📦Package — code live
doneCheck — all green
failedX — terminal failure
cancelled🚫Forbidden — manual cancel
stuck🐌Snail — stuck-watcher fired (rc#232)
escalated🚨Siren — needs human (rc#258)

Rules:

  • The primary emoji is always the leftmost token on line 1.
  • It changes on every lifecycle state transition.
  • stuck overrides in_progress (see edge cases §4).
  • escalated overrides all other non-terminal states.

The second line begins with the tier emoji from Planner output (rc#157).

TierEmojiMeaning
T1 reversible🟢Low blast radius
T2 schema🟡Needs care
T3 irreversible🔴Human approval required

The tier is set at issue-open time and does not change during the lifecycle.

Modifiers are appended to the status row (line 2), after the tier, separated by ·. Multiple modifiers compose in the order listed.

SignalEmojiTrigger condition
Cost > $2💰Budget concern (rc#316)
Cost > $5🔥Hot — investigate
Turns > 50⏱️Time concern
COI / scope mismatch⚠️Scope-match gate fired (rar#155)
Reverted↩️Revert-retro fired (rc#392)

Notes:

  • 💰 and 🔥 are mutually exclusive; 🔥 supersedes 💰 when cost > $5.
  • Modifiers accumulate; they are not cleared when state changes.
  • The status row must not exceed 300 characters (see §5 message length).

The Conductor edits the same Discord message in-place on every state transition. The format is:

{primary_emoji} 📋 **{repo}#{number}** — {title}
{tier_emoji} {tier_label} · {state_label}{agent_clause}{metric_clause}{modifier_clause}
{issue_url}

Where:

PlaceholderDescription
{primary_emoji}State emoji from §1
{repo}Short repo name (e.g. rig-conductor)
{number}Issue number
{title}Issue title, truncated at 80 chars with
{tier_emoji}Tier emoji from §2
{tier_label}T1, T2, or T3
{state_label}Human-readable state slug (e.g. in_progress, done)
{agent_clause} · {agentId} when an agent is assigned; omitted otherwise
{metric_clause} · turn {n} · ${cost} when in-flight; · {duration} · ${total_cost} when terminal
{modifier_clause}Zero or more · {emoji} pairs from §3, appended in table order
{issue_url}Full GitHub issue URL

Concrete examples:

Created (queued):

⏳ 📋 **rig-conductor#420** — feat: extend lifecycle with deploy-confirm
🟡 T2 · queued
https://github.com/dashecorp/rig-conductor/issues/420

In progress:

🛠️ 📋 **rig-conductor#420** — feat: extend lifecycle with deploy-confirm
🟡 T2 · in_progress · dev-e-dotnet · turn 47 · $0.42
https://github.com/dashecorp/rig-conductor/issues/420

Done:

✅ 📋 **rig-conductor#420** — feat: extend lifecycle with deploy-confirm
🟡 T2 · done · 24m · $1.18 · PR #421 merged
https://github.com/dashecorp/rig-conductor/issues/420

Failed:

❌ 📋 **rig-conductor#420** — feat: extend lifecycle with deploy-confirm
🟡 T2 · failed · dev-e-dotnet timeout @ turn 105
https://github.com/dashecorp/rig-conductor/issues/420

Stuck with modifiers:

🐌 📋 **rig-conductor#420** — feat: extend lifecycle with deploy-confirm
🟡 T2 · in_progress · stuck-watcher (3rd occurrence) · ⏱️ 🔥
https://github.com/dashecorp/rig-conductor/issues/420

Discord rate-limit is 5 edits / 5 s per channel. When multiple state transitions arrive within a 1-second window for the same issue, the renderer coalesces them and emits only the latest state. Implementation must use a per-issue debounce window of 1 second.

When the stuck-watcher fires (rc#232) while an issue is in_progress:

  • Set primary emoji to 🐌 (overrides 🛠️).
  • State label remains in_progress with annotation · stuck-watcher ({n}th occurrence).
  • If work resumes (cli_progress event fires within 10 minutes of stuck detection), revert to 🛠️ and remove the annotation.
  • A third occurrence with no resume within 10 minutes escalates to 🚨 via the rebut-cap (rc#258).

When escalated state is reached via rc#258:

  • Set primary emoji to 🚨 regardless of prior state.
  • This is a non-terminal state — the issue may return to in_progress if a human intervenes and re-dispatches.

If the Conductor cannot locate the original Discord message (deleted, channel gone, etc.):

  • Fall back to posting a new message in the channel.
  • Log a WARN with the original message ID and reason.
  • Continue tracking the new message ID for future edits.

Discord max is 2000 characters. The three-line template must fit within:

LineMax charsOverflow behaviour
Line 1 (emoji + title)~120Truncate title at 80 chars with
Line 2 (status row)300Drop modifier emojis right-to-left until it fits
Line 3 (URL)~80Never truncated

If line 2 still exceeds 300 chars after dropping all modifiers, truncate {metric_clause} to · ${cost} only.

Use the canonical thread of the originating issue (per rc#425 unified-thread fix). State emoji follows the upstream issue, not chore PRs or sub-issues.

During iterative changes_requestedin_progress cycles:

  • Stay on the active primary emoji (✏️ or 🛠️) as appropriate.
  • Do not flip to ❌ until a terminal failure event is received.
  • Do not flip to 🚨 until rc#258 rebut-cap fires.

Before making a Discord API edit, compare the new rendered string against the last-known rendered state. If identical, skip the edit. This prevents needless API calls and rate-limit consumption on duplicate events.

This feature applies forward only. Existing #tasks posts (pre-feature) remain as static text. No migration or backfill is required or desired.


Accepted. This vocabulary and format are the canonical schema for all downstream implementation work in the emoji-status epic (rc#433).


RefDescription
rc#433Epic: emoji status indicators
rc#413deploy-confirm lifecycle (🚀 / 📦 states)
rc#316Cost budget (💰 / 🔥 modifiers)
rc#392Revert-retro (↩️ modifier)
rc#232Stuck-watcher (🐌 state)
rc#258Rebut-cap (🚨 escalation)
rc#425Unified per-issue Discord thread
rar#155Review-E scope-match gate (⚠️ modifier)
rc#157Planner tier output