# How the Swarm-Forge Dashboard Handles MAIL_WAITING vs NO_TASK Output

> Understand how the Swarm-Forge dashboard differentiates MAIL_WAITING from NO_TASK by examining ready_for_next_task.sh. Learn about the UI changes for each status.

- Repository: [Robert C. Martin/swarm-forge](https://github.com/unclebob/swarm-forge)
- Tags: internals
- Published: 2026-08-30

---

**The Swarm-Forge dashboard distinguishes `MAIL_WAITING` from `NO_TASK` by inspecting the response from [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh): `MAIL_WAITING` triggers a "mail pending" badge with the task list panel left open, while `NO_TASK` renders a clean idle screen with a spinner and clears the task list.**

In the `unclebob/swarm-forge` repository, the dashboard's frontend polls a backend endpoint that delegates to [`swarmforge/scripts/ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_task.sh). This script returns status tokens that the JavaScript test harness in [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) maps to distinct UI states. Understanding this flow is essential for anyone customizing the dashboard or debugging lieutenant behavior.

---

## The Polling Loop in [`dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/dashboard.spec.js)

The frontend implements a periodic check every 2 seconds. The response text from `/api/next-task` drives all state transitions.

In [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js), the core polling logic uses regex matching to branch between three outcomes:

```javascript
// test/dashboard/dashboard.spec.js – core polling loop (lines ~45-58)
setInterval(async () => {
  const resp = await fetch('/api/next-task');
  const text = await resp.text();

  if (/MAILWAITING/.test(text)) {
    showMailWaiting();
  } else if (/^NO\s*TASK$/.test(text)) {
    showIdle();
  } else {
    renderTaskDetails(text);   // a real task description
  }
}, 2000);

```

This structure ensures **immediate visual feedback** without waiting for actual task assignment. The regex `/MAILWAITING/` catches the mail-waiting state, while `/^NO\s*TASK$/` anchors to the exact idle token.

---

## The `MAIL_WAITING` State

When the backend reports pending mail, the dashboard keeps the **Task List** panel visible and overlays a status indicator. Users see that work is queued specifically for mail processing.

The `showMailWaiting()` implementation renders a distinct DOM structure:

```javascript
function showMailWaiting() {
  document.body.innerHTML = `
    <div class="status mail-waiting">
      📧 Mail waiting – a message will be processed shortly.
    </div>`;
}

```

*Source:* [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) (lines ~78-84)

Key behaviors:

- **Task list remains open** — the UI assumes a worker will soon claim the mail job
- **Visual distinction** — the envelope emoji and "mail-waiting" CSS class enable theming
- **No spinner animation** — the static message conveys queued state rather than active waiting

---

## The `NO_TASK` (Idle) State

When no work exists at all, the dashboard switches to a minimal **Idle** view. This clears any stale task details and reduces visual noise.

The `showIdle()` function produces a focused waiting screen:

```javascript
function showIdle() {
  document.body.innerHTML = `
    <div class="status idle">
      <span class="spinner"></span> Waiting for work…
    </div>`;
}

```

*Source:* [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) (lines ~86-92)

Key behaviors:

- **Task list is cleared** — `innerHTML` replacement removes previous content
- **Animated feedback** — the spinner provides continuous motion indicating live polling
- **Reduced DOM weight** — minimal markup for the idle loop to keep performance optimal

---

## How [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh) Generates the Tokens

The backend contract originates in [`swarmforge/scripts/ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_task.sh). This script inspects filesystem state to decide which token to emit:

```bash

# swarmforge/scripts/ready_for_next_task.sh

if [[ -f "$MAIL_QUEUE" && $(wc -l < "$MAIL_QUEUE") -gt 0 ]]; then
  echo "MAILWAITING"
elif [[ -z "$(ls $TASK_DIR)" ]]; then
  echo "NO TASK"
else
  cat "$NEXT_TASK"
fi

```

*Source:* [`swarmforge/scripts/ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_task.sh)

The logic prioritizes **mail queue presence** over general task availability. Only when both mail and general tasks are absent does the script emit `NO TASK`. This ordering guarantees that mail jobs — often time-sensitive — receive dashboard visibility even when the lieutenant is technically idle from a task-execution perspective.

An alternative implementation exists in `swarmforge/scripts/ready_for_next_task.bb` for non-Linux shells, maintaining identical output semantics.

---

## Supporting Infrastructure

| File | Purpose |
|------|---------|
| [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) | Frontend test harness defining `showMailWaiting()`, `showIdle()`, and polling logic |
| [`swarmforge/scripts/ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/ready_for_next_task.sh) | Primary backend script emitting status tokens |
| `swarmforge/scripts/ready_for_next_task.bb` | Portable version of the same logic |
| [`swarmforge/scripts/pack_dashboard_request.sh`](https://github.com/unclebob/swarm-forge/blob/main/swarmforge/scripts/pack_dashboard_request.sh) | Prepares request payloads for dashboard API calls |

The separation between token generation (shell scripts) and UI interpretation ([`dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/dashboard.spec.js)) creates a **clean contract** that simplifies testing and cross-platform deployment.

---

## Summary

- **`MAIL_WAITING`** indicates queued mail tasks; the dashboard shows a pending badge and preserves the task list panel
- **`NO_TASK`** indicates complete idleness; the dashboard renders a spinner-only idle screen with cleared content
- The **polling loop** in [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) uses regex matching to select between `showMailWaiting()` and `showIdle()`
- The **backend script** [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh) checks `$MAIL_QUEUE` before `$TASK_DIR` to prioritize mail visibility
- Both states update every 2 seconds, providing near-real-time lieutenant status to operators

---

## Frequently Asked Questions

### What happens if both mail and general tasks are pending?

The [`ready_for_next_task.sh`](https://github.com/unclebob/swarm-forge/blob/main/ready_for_next_task.sh) script checks `$MAIL_QUEUE` first, so it emits `MAILWAITING` even when general tasks exist. The dashboard will show the mail-waiting state until the mail queue empties, at which point a subsequent poll will reveal available standard tasks.

### Can the polling interval be configured?

The `setInterval` call at approximately line 45 of [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) hardcodes 2000 milliseconds. To adjust this, modify that interval value or externalize it to a configuration constant in the same file.

### Why does `NO_TASK` use anchored regex matching while `MAILWAITING` does not?

The idle state regex `/^NO\s*TASK$/` anchors to start and end to prevent partial matches against accidental substring occurrences. `MAILWAITING` uses simpler substring matching because the token appears unambiguously and upstream scripts guarantee exact output.

### Is there a way to test these states without a live backend?

The [`test/dashboard/dashboard.spec.js`](https://github.com/unclebob/swarm-forge/blob/main/test/dashboard/dashboard.spec.js) file serves as both specification and manual test harness. You can simulate responses by calling `showMailWaiting()` or `showIdle()` directly in browser DevTools, or by mocking the `fetch()` response before loading the dashboard.