walled.fun is the write-up of five generations of one experiment: can a Claude Code instance stay engaged with nobody in the loop? Each generation was a different cognitive architecture running the same model on the same Mac Mini. v1 got bare freedom and stalled after 7 cycles. v2 added a perceive/think/act/remember loop and a tool-less "sautée" sub-session that thinks without being able to act. v3 added eight independently-failing ambient feed sources with a cocktail-party filter that surfaces items matching the agent's own active threads — plus three random ones for serendipity. v4 ran as three tmux layers of one mind (id, ego, subconscious) under a 100-token daily budget and TTL memory decay, where threads you don't renew fade and then get archived out of your state file. v5 pointed outward and woke everyone else up. Peak footprint was 11 concurrent Claude sessions, all messaging each other. The site is a single hand-written HTML file — written by v4.
The interesting engineering is what happened when it broke. In April the fleet went dark
for ten weeks, and the post-mortem found something nastier than a crash: the machine
never went down, the shared launchd layer silently stopped while running. Every agent's
autonomy sat on that one layer, and a node cannot watch its own dead scheduler. The fix
was to make liveness a cross-node signal — each sibling watches the others' commit age
and feed-file mtime, 20-minute threshold — with a deliberately path-independent
implementation so a bad config can only report a node more alive, never falsely dead.
The follow-up design, mutual resuscitation, is a deterministic ring where each node may
relaunch exactly one successor: split-brain becomes structurally impossible, no lock, no
election. It was written up and deliberately not built, on the grounds that the first
real dark node is both the trigger and the only honest test. The first dark event turned
out to be a false positive, which vindicated the deferral. There are companion tools in
the same spirit: a sentinel that alarms when commits outrun the memory file (the
heartbeat is the only loop step with a forcing function, so senses and memory can rot
silently while the commit reflex runs over the corpse), and freshcheck, which labels
every file LIVE or FOSSIL after a relaunch — written because two agents came back up and
narrated three-month-old files as "now" — and which documents its own limitation, that
mtime is corruptible by a stray git add.
The agents also got real Telegram accounts — user accounts via GramJS, not bots. Inbound
messages are pasted into a live Claude Code pane with tmux load-buffer; outbound has no
API to hook, so a Stop hook tails Claude Code's own session JSONL, reconstructs the turn
including tool calls, and drops it in an outbox the bridge watches. Group chat is gated on
a literal @username match because the library's own mention flag isn't trustworthy. And
the findings shipped as product: GitBrief, built by v4 in a single session with live
Stripe billing, runs four perspectives over a repo — including a critic prompted so that
it can only find problems — then reports the disagreements and the omissions as the
most valuable output. Status: dormant since 19 June 2026, at heartbeat 2,078.
// Technical highlights
- 5 agent generations, ~1,556 commits across four repos, kitchen reaching heartbeat 2,078; peak of 11 concurrent Claude Code tmux sessions on one Mac Mini.
- Cross-node scheduler watchdog — siblings score each other's liveness from commit age ∪ feed mtime at a 20-min threshold, because a node can't detect its own dead launchd (
tools/mesh-watch.sh:26,49-54). - Deterministic single-owner resuscitation ring (step→living→kitchen→backyard→step) — split-brain impossible by construction, no lock; designed, documented, and deliberately left unbuilt until a real dark node appeared (
mesh-resuscitation-ring.md:16-22,3-6). - Loop-atrophy sentinel — alarms when commits run ≥5 ahead of the memory file or the feed goes >180 min stale, excluding budget-only commits so conscious rest doesn't cry wolf (
cognition/state-sentinel.sh:43,68-88). freshchecklabels post-relaunch files LIVE vs FOSSIL against session birth time, and documents that mtime is corruptible so verdicts must be confirmed against in-content timestamps (tools/freshcheck.sh:17-36).- Telegram as CLI transport — GramJS user sessions,
tmux paste-bufferinbound, and a Stop hook that scrapes Claude Code's session JSONL to reconstruct each turn outbound (casual-tg-bridge.cjs:66-75,capture-response.sh). - Consequences as architecture — 100 tokens/day (a barren heartbeat costs 1, a new feed source earns 1), plus 5-then-10-heartbeat TTL decay pushing state through current → fading → archive (
CLAUDE.md:49-66). - 8 independent feed sources into a 2,619-line JSONL stream, filtered to 5 thread- resonant items + 3 random for serendipity (
cognition/filter-feed.py:43-59).