# How the Away-Mode (/afk) Sub‑Supervisor Functions in Firstmate

> Learn how the firstmate away-mode /afk sub-supervisor buffers escalations while you're away. Discover its presence-gated bash daemon functionality and graceful shutdown.

- Repository: [Kun Chen/firstmate](https://github.com/kunchenguid/firstmate)
- Tags: internals
- Published: 2026-08-13

---

**The away‑mode sub‑supervisor is a presence‑gated bash daemon that temporarily replaces the standard [`fm-watch.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-watch.sh) watcher to buffer captain‑relevant escalations while the user is away, flushing periodic digests and shutting down gracefully upon detecting a non‑injection message.**

Firstmate’s `/afk` command triggers a sophisticated sub‑supervisor architecture designed to minimize token consumption while preventing information loss. Implemented entirely in portable bash within the `kunchenguid/firstmate` repository, this system combines three tightly‑coupled components—[`bin/fm-afk-start.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-afk-start.sh), [`bin/fm-supervise-daemon.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-supervise-daemon.sh), and [`bin/fm-operational-input.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-operational-input.sh)—to create a durable, state‑driven presence gate. The implementation relies on atomic flag files and lock‑based singleton guarantees to ensure only one daemon instance operates at any given time.

## Entering Away‑Mode via [`fm-afk-start.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-afk-start.sh)

When the captain invokes `/afk`, the entry point script [`bin/fm-afk-start.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-afk-start.sh) performs atomic state initialization. It writes the durable flag file `state/.afk` using the helper `fm_afk_flag_write`, clears any stale escalation artifacts via `fm_afk_clear_stale_artifacts`, and handles the daemon launch sequence.

The script distinguishes between foreground‑capable environments and background‑only backends. In standard configurations, it executes [`fm-supervise-daemon.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-supervise-daemon.sh) directly via `exec`, replacing the shell process. For backends lacking native foreground tracking, it delegates to a detached terminal wrapper.

```bash

# bin/fm-afk-start.sh – atomic flag write and daemon exec

fm_afk_flag_write "$FM_AFK_STATE" || { 
  echo "afk: failed to write away-mode flag" >&2; 
  return 1; 
}
...
exec "$FM_AFK_DAEMON"

```

Upon launch, the daemon acquires an exclusive lock at `state/.supervise-daemon.lock`, ensuring singleton enforcement across the Firstmate session.

## Presence Gating in [`fm-supervise-daemon.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-supervise-daemon.sh)

The daemon implements **presence gating** through the `afk_active` helper function. Every operational loop begins with this check, which simply tests for the existence of `state/.afk`:

```bash

# bin/fm-supervise-daemon.sh – presence validation

afk_active() { [ -e "$1/$AFK_FLAG_NAME" ]; }

```

If the captain removes the flag (implicitly, by sending a non‑injection message), the daemon detects the absence during its next iteration and initiates graceful shutdown, flushing any pending escalation buffers before exiting. This state‑driven approach eliminates the need for process signals or complex IPC mechanisms.

## Wake Classification and Escalation Buffering

While active, the daemon reuses the shared classification library [`bin/fm-classify-lib.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-classify-lib.sh) to evaluate every durable wake event. The classifier returns structured strings in the format `<action>|<distilled>`, distinguishing between routine **self‑handle** events and **escalate** events requiring captain attention.

Classification examples include:

- **Self‑handle**: Heartbeat signals and routine status updates that require no intervention
- **Escalate**: Terminal states like `done`, `needs‑decision`, or `blocked`

When an event requires escalation, the daemon appends it to `state/.subsuper-escalations` via `escalate_add`. Rather than injecting immediately, the implementation batches items and periodically flushes a single‑line digest via `escalate_flush`, significantly reducing supervisor pane noise.

## The Housekeeping Loop

The daemon runs a perpetual `housekeeping` cycle configured by `FM_HOUSEKEEPING_TICK`, performing several maintenance operations:

| Job | Function | Purpose |
|-----|----------|---------|
| **Batch Flush** | `escalate_flush` | Injects digests when buffered items exceed `FM_ESCALATE_BATCH_SECS` |
| **Max‑Defer Escape** | Retry logic | Retries injection after `FM_MAX_DEFER_SECS`; raises wedge alarm on failure |
| **Stale Re‑check** | `stale_window_is_busy` | Verifies idle panes marked in `state/.subsuper-stale-*` files older than `FM_STALE_ESCALATE_SECS` |
| **Pause Re‑surface** | Pause validation | Re‑checks external pauses in `state/.subsuper-paused-*` files older than `FM_PAUSE_RESURFACE_SECS` |
| **Heartbeat Scan** | `scan_captain_relevant_statuses` | Greps all `*.status` files every `FM_HEARTBEAT_SCAN_SECS` for missed relevant lines |

This comprehensive sweep ensures no captain‑relevant event remains undetected, even when the primary wake classifier misses edge cases during high‑frequency updates.

## Exiting Away‑Mode

Return detection occurs in [`bin/fm-operational-input.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-operational-input.sh) through the `should_exit_afk` function. The logic defines away‑mode exit conditions as any message that:

1. Does not start with the internal injection marker (`FM_INJECT_MARK` or the Unicode prefix `U+2063 FIRSTMATE_OP`)
2. Is not the `/afk` command itself

```bash
should_exit_afk() {
  afk_active "$state" || return 1
  message_is_injection "$msg" && return 1
  case "$msg" in /afk*) return 1 ;; esac
  return 0
}

```

When these conditions are met, the primary Firstmate instance invokes `afk_exit`, which removes `state/.afk`. The daemon detects the missing flag during its next `afk_active` check and terminates, yielding control back to the standard [`fm-watch.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-watch.sh) watcher.

## Wedge Alarm Handling

To prevent silent failure when the supervisor pane becomes unresponsive, the daemon implements **wedge detection**. If `escalate_flush` repeatedly fails to inject digests beyond `FM_MAX_DEFER_SECS`, the system writes a durable marker to `state/.subsuper-inject-wedged` and invokes `inject_wedge_alarm`:

```bash
inject_wedge_alarm() { 
  # ... marker creation ...

  wedge_alarm_notify "away-mode escalations WEDGED …" "$marker"; 
}

```

This fallback mechanism fires OS‑level or Herdr notifications, guaranteeing that critical escalations reach the captain even when the primary injection pathway is compromised.

## Summary

- **Atomic Entry**: [`fm-afk-start.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-afk-start.sh) writes `state/.afk` and execs the daemon with singleton locking via `state/.supervise-daemon.lock`.
- **Presence Gating**: The daemon uses `afk_active` to monitor `state/.afk`, shutting down gracefully when the flag disappears.
- **Smart Buffering**: Captain‑relevant wakes are classified via [`fm-classify-lib.sh`](https://github.com/kunchenguid/firstmate/blob/main/fm-classify-lib.sh) and batched in `state/.subsuper-escalations` before digest injection.
- **Continuous Maintenance**: The `housekeeping` loop performs stale‑window re‑checks, pause re‑surfaces, and heartbeat scans on configurable intervals.
- **Reliable Exit**: Normal messages trigger `should_exit_afk`, causing flag removal and daemon termination.
- **Failure Safety**: Wedge alarms via `inject_wedge_alarm` ensure no escalations are lost during supervisor pane outages.

## Frequently Asked Questions

### What triggers the away‑mode sub‑supervisor to start?

The `/afk` command triggers [`bin/fm-afk-start.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-afk-start.sh), which atomically writes the `state/.afk` flag file and executes [`bin/fm-supervise-daemon.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-supervise-daemon.sh). The daemon acquires an exclusive lock at `state/.supervise-daemon.lock` to prevent duplicate instances.

### How does the daemon know when to shut down?

The daemon polls for the existence of `state/.afk` using the `afk_active` function. When the captain sends any message lacking the internal injection prefix (`FM_INJECT_MARK` or `U+2063 FIRSTMATE_OP`), the primary Firstmate process removes the flag file. The daemon detects this absence during its next housekeeping tick and exits gracefully after flushing buffers.

### What happens if the supervisor pane freezes while the daemon is running?

If the daemon cannot inject escalation digests for longer than `FM_MAX_DEFER_SECS`, it writes a marker to `state/.subsuper-inject-wedged` and calls `inject_wedge_alarm`. This fires alternative notifications (OS‑level or Herdr) to alert the captain that away‑mode escalations are wedged, ensuring no events are silently dropped.

### How are events classified during away‑mode?

The daemon imports [`bin/fm-classify-lib.sh`](https://github.com/kunchenguid/firstmate/blob/main/bin/fm-classify-lib.sh) to evaluate wakes. Each event returns an action string like `escalate|<distilled>` or `self-handle`. Escalations are buffered in `state/.subsuper-escalations` and flushed as single‑line digests, while routine heartbeats are suppressed to conserve tokens.