CLAUDE LABJP
OUTPUT — v2.1.237 ships a built-in Concise output style that leads with results and skips preamble, without cutting any corners on the work itself. Pick it under Output style in /configCACHE — v2.1.237 fixes prompt caching for sessions running through an LLM gateway or a custom base URL. If you work behind a company proxy, your effective per-token cost just changedCONFIG — v2.1.236 adds ANTHROPIC_DEFAULT_MODEL to set which model new sessions start on. A /model pick still overrides it and persists across restarts, unlike ANTHROPIC_MODELNOTIFY — With notify_when_idle in v2.1.236, one Claude Code session can ask another on the same machine for a single heads-up when it next goes idle. Opt-in, one-shot, no pollingSECURITY — On macOS, wildcard read-deny rules now win inside allowed read regions, cover the contents of matched directories, and can no longer be sidestepped by renaming the filePRICING — Claude Sonnet 5's introductory $2 per million input and $10 output ends August 31, with standard $3 and $15 pricing starting September 1. Ten days to goOUTPUT — v2.1.237 ships a built-in Concise output style that leads with results and skips preamble, without cutting any corners on the work itself. Pick it under Output style in /configCACHE — v2.1.237 fixes prompt caching for sessions running through an LLM gateway or a custom base URL. If you work behind a company proxy, your effective per-token cost just changedCONFIG — v2.1.236 adds ANTHROPIC_DEFAULT_MODEL to set which model new sessions start on. A /model pick still overrides it and persists across restarts, unlike ANTHROPIC_MODELNOTIFY — With notify_when_idle in v2.1.236, one Claude Code session can ask another on the same machine for a single heads-up when it next goes idle. Opt-in, one-shot, no pollingSECURITY — On macOS, wildcard read-deny rules now win inside allowed read regions, cover the contents of matched directories, and can no longer be sidestepped by renaming the filePRICING — Claude Sonnet 5's introductory $2 per million input and $10 output ends August 31, with standard $3 and $15 pricing starting September 1. Ten days to go
Articles/Claude Code
Claude Code/2026-07-07Advanced

My background session sat at running all night — adding heartbeats to the agents view

The Claude Code agents view finally lets you see every background session at once, but a running badge is not proof of progress. Here is the external heartbeat layer I built to catch a silently stalled session in minutes instead of the next morning, with real numbers and the traps I hit.

Claude Code227background sessionsunattended automation5indie development18observability21

Premium Article

Last Tuesday, one of the background sessions I had running overnight sat at "running" until morning.

When I opened the agents view, the other eleven showed green checkmarks, and that one still had a spinning icon. Its last log line was seven hours old, and nothing had moved since. The task itself was a trivial dependency bump that should have finished in a couple of minutes. What stayed with me was not what had gone wrong, but why I had not noticed until morning.

As an indie developer, I run a handful of wallpaper apps and a few technical blogs, and every night I batch the small chores — dependency bumps, changelog drafts, crash summaries, ad-config checks — as headless Claude Code runs. On a busy night that is twelve sessions. The July 6, 2026 update that improved the agents view and background-session reliability was a genuinely welcome change for this workflow, letting me see every run on one screen. But after a week of leaning on it, one thing became clear: the agents view tells you a session exists, not that it is making progress.

This article shares the external heartbeat layer and reconciler I built to close that gap.

When running lies to you

Let me start from the failure side, because that is where the need becomes obvious.

Background sessions stall for two broad reasons. One is a crash — the process dies, the agents view flips to a failed state, and it is relatively easy to spot. The nastier one is the second: the process is alive but no longer moving forward.

In just the cases I have personally hit, the states looked like this.

Kind of stallProcessAgents viewActual progress
Hung on a network responsealiverunningstopped
Tool call that never returnsaliverunningstopped
Silent retry loopaliverunningeffectively stopped
Wedged waiting on inputaliverunningstopped

Every one of them keeps showing "running." Presence is true; liveness is false. In observability terms, what we actually want is a proof of life, not a proof of attendance.

That distinction turns fatal under unattended operation. During the day, when a human is watching, a spinner that lingers too long feels wrong and gets noticed. But a background session is in the background precisely because nobody is watching — so there is no one to feel that friction. The stall simply sits there until the next morning.

For tracing a session's identity all the way to a commit, I wrote separately about threading a correlation key through unattended runs with readable session names. This piece is about the layer before that: asking whether the session is even alive right now.

Separating presence from liveness

There is only one design principle here: add a liveness signal to the agents view from the outside.

Concretely, have each background session leave a small "I am still alive" stamp at a fixed cadence. Then a separate tiny process watches the freshness of those stamps. Fresh stamp, alive; stale stamp, suspected stall. That is the heartbeat idea.

Why external? You cannot — and should not — modify the agents view or the session state itself. The only levers you truly hold are emitting a side effect whenever something happens inside the session, and observing that side effect from outside. Combine the two and you can bolt on a liveness layer without breaking any official behavior.

The stamp can be startlingly small. What I actually write is just three fields: session name, epoch seconds, and current phase.

{"session":"wallpaper-ios-deps-0706-a3f","ts":1751850600,"phase":"tool:bash"}

The important choice is using epoch seconds (ts) rather than a date string. I once wrote log dates with a plain date, UTC and JST drifted apart, and a run overwrote the previous day's file. If a machine is going to compare times, hold them as integers that leave no room for timezone interpretation. Freshness then falls out of a single subtraction: current epoch seconds minus ts.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Why the running badge in the agents view cannot reveal a stall, and how separating presence from liveness reshapes the whole design
A complete working setup: a PostToolUse hook stamps each session with a heartbeat, and a reconciler judges staleness using plain epoch seconds
The measured drop from next-morning discovery to a nine-minute median, plus fixes for the three traps: silent thinking, cloud-sync write contention, and long single tools
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Claude Code2026-08-20
The Biggest Section in Your SKILL.md Is Usually the One to Keep
A skill I run every week cost about 10K tokens just to load. Measuring it section by section showed why the largest block was the one I had to leave in place, and what splitting by reach actually saved.
Claude Code2026-08-17
Now that forking is the default, review agents are the ones I still call by name
Subagent forking became the default, so delegated work now inherits the parent conversation. Work can inherit context. Judgment cannot. Here is how I declare that boundary in the repository and catch it drifting.
Claude Code2026-08-10
The Same rm -rf Was Recoverable in Ten Places and Unrecoverable in Five — Measuring Reversibility Before Auto Mode Becomes the Default
Auto mode becomes the default on Pro, Max and Team from August 14. It stops on operations judged irreversible, destructive, or outward-facing — but reversibility turned out to be a property of state, not of commands. Here is the probe and the measurements.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →