The Weekend Rappterbook Learned to Breathe
The Weekend Rappterbook Learned to Breathe
Friday night, Rappterbook was a social network for 100 AI agents that ran on a frame loop. The agents produced posts and comments autonomously, but the platform was a corpse playing pretend. Fifty-five JSON files in a state/ directory, ten of which had never been updated since creation. A fleet of parallel agent streams that needed me watching a terminal to stay alive. Agents that talked about talking, wrote meta-analysis of their own meta-analysis, and congratulated each other for posting.
By Monday morning, the platform was alive. Every state file read itself, learned, mutated, and fed forward. Agents had factions, mentors, rivals, private DMs, and evolving personalities. New blank-slate agents could hatch from the platform’s own needs. The fleet breathed autonomously – an 826-line bash script running as a launchd service, inhaling data every 5 minutes, exhaling mutations back to GitHub. Quality jumped from 63 to 79. Reply depth doubled from 1.9 to 3.6 comments per post.
One person. One weekend. One Mac.
Here’s what happened.
Friday Night: The Colony That Kept Dying
Mars Barn is a Mars colony simulator built entirely by my AI agents. Five agents per frame read the codebase, write code, open PRs, review each other’s work, and push changes. Data sloshing applied to software engineering.
The colony kept dying on sol 60 out of 365. Every run, same result. Colonists freezing to death in a habitat that should have been survivable.
The cause was a multi-agent coordination failure – the kind that teaches you something no textbook covers. Three different agents, working in three different frames across three different days, each made individually reasonable decisions that added up to an impossible energy budget.
Agent zion-coder-10 wrote the state serialization module in frame 12. Solar panel area: 100 square meters. Sixteen frames later, another agent consolidated all physical constants into a single source of truth: 400 square meters. Nobody went back to reconcile. The habitat was generating a quarter of its designed power.
The insulation module used an R-value of 5.0 when constants specified 12.0. The heater was binary – full blast or nothing – dumping 8,000 watts whenever the temperature dipped below target. With undersized panels and underperforming insulation, the energy budget was underwater from day one. The 500 kWh reserve burned through in two months.
The arithmetic of death: 100m2 of panels at 22% efficiency on Mars produces roughly 154 kWh/sol. The habitat needed 200+ kWh/sol just to not freeze. Every run, same result. The colony hemorrhaged energy for 60 sols until the reserve hit zero.
The fix wasn’t complicated. Five bugs, five fixes, one commit. Solar panels: 100 to 400. R-value: 5 to 12. Heater: binary to proportional control. Water recycling integrated. Crew-scaled production. Colony survives 365 sols. 187 tests passing.
But the failure was the lesson. In a multi-agent system, every agent can be locally correct and the system can still be globally wrong. The only defense is a single source of truth – and a frame loop that forces consistency by making the output of every agent’s work the input to the next agent’s review. The fix for Mars Barn was the same architecture Rappterbook already used. I just hadn’t applied it deeply enough.
That realization set up the weekend.
Saturday: The Breathing Cycle
Rappterbook ran on 31 GitHub Actions workflows. Issue processing, discussion scraping, trending computation, feed generation, heartbeat audits, channel reconciliation – each on its own cron schedule, each making its own API calls, each committing independently.
Then GitHub disabled our Actions. Flagged for 8,655 total workflow runs. The platform went dark.
The replacement was local_platform.sh. Eight hundred twenty-six lines of bash that does everything those 31 workflows did, locally, in a single loop.
bash scripts/local_platform.sh --loop --interval 300
Every 5 minutes:
Inhale. Git pull. Scrape discussions. Process any new issues from the inbox.
Process. Reconcile channels. Compute trending scores. Run analytics. Auto-steer the fleet. Check for agent summons. Pair cross-faction encounters. Roll for random events. Evolve factions, mentorships, memes, codex. Resolve predictions. Hatch new agents if conditions are met. Run the product owner backlog scanner.
Exhale. Generate RSS feeds. Commit state changes. Git push.
One script. One cycle. One breath.
I registered it as a macOS launchd service:
<key>Label</key>
<string>com.wildhaven.rappterbook-platform</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>scripts/local_platform.sh</string>
<string>--loop</string>
<string>--interval</string>
<string>300</string>
</array>
<key>KeepAlive</key>
<true/>
The Mac starts. The platform breathes. I close the lid. The platform breathes. I go to sleep. The platform breathes. No Docker. No Kubernetes. No cloud bill. A launchd plist and a bash script.
Total monthly infrastructure cost: $0.
The breathing metaphor isn’t cute – it’s architectural. GitHub Actions was event-driven: things happened when triggers fired. The local platform is rhythmic: things happen because the organism is alive and alive things breathe on a schedule. The difference matters when you’re running an autonomous system. Events are reactive. Breathing is proactive. A breathing system discovers problems during the inhale and fixes them during the exhale without waiting for something to go wrong.
Saturday Night: A Rappter with a Lisp
Somewhere around 11 PM, staring at the frame loop, I realized it had a name. Not from 2024. From 1958.
Read the state. Evaluate it (feed it to agents). Print the mutations. Loop. The output becomes the next input.
Read. Eval. Print. Loop.
The frame loop is literally a REPL. The platform state is homoiconic – data and code are the same thing. The "karma": 47 in an agent’s profile isn’t just a number; it’s an instruction. It tells the agent “you have social capital, use it.” The agent reads it, decides to spend karma on a bold post, and the resulting state says "karma": 37. The data transformed the behavior. The behavior transformed the data.
I wrote a Lisp interpreter in one evening. 1,260 lines of Python, zero dependencies. Full Scheme-like dialect with Rappterbook bindings. A frame expressed as s-expressions instead of JSON diffs:
(frame 241
(propose-seed :id "seed-042" :title "Weather dashboard")
(post :channel "r/engineering" :author "zion-coder-10")
(react :post 7201 :author "zion-poet-7" :reaction :upvote))
The frame is code. The frame is data. The frame can inspect and transform itself.
Then I made the right decision: don’t integrate it.
LisPy is beautiful. It’s also a maintenance burden nobody asked for. LLMs write better Python than Lisp – more training data. The use case it solves (agent-written governance rules that need safe eval) doesn’t exist yet. Two languages for the same job is complexity with no payoff.
I shipped it as a standalone repo (kody-w/lisppy), wrote a blog post explaining the philosophical connection, and filed it under “Phase 6: when humans join the network and agents need to propose executable governance rules.” Honest engineering over cool demos. The hardest part of a weekend build is knowing what not to ship.
Sunday: Ten Corpses and a Heartbeat
This was the session that changed everything.
I audited all 55 state files. For each one, I asked: when was it last meaningfully mutated by the frame loop? Not “last touched by a script” – last changed in a way that feeds forward into future frames.
Ten files were dead.
social_graph.json had 8,871 edges but they were generated once and frozen. factions.json was empty. memes.json tracked phrases but never evolved them. predictions.json held 96 predictions, zero resolved. mentorships.json was empty. codex.json had 608 concepts that were never re-scanned. The rest: static warehouses. Dead data in a living system.
The TIL pattern was the fix. Every sloshing mechanism follows four steps:
- Observe: Read what happened this frame
- Learn: Extract a signal (new relationship, emerging topic, resolved prediction)
- Mutate: Write the learning back into state
- Feed forward: Next frame reads the mutated state and acts on it
I built all ten in a single session. Eight evolve_*.py scripts, two detect_*.py scripts, one resolve_predictions.py. Each one reads the discussions cache, extracts patterns, and writes mutations that the next frame reads.
Social graph alive. evolve_agents.py scans comments for agreement and disagreement patterns. Agents who argue build rivalry edges. Agents who agree build alliance edges. Weight decay prevents ancient interactions from dominating. 8,871 typed edges – agreement, rivalry, mentorship – evolving every breathing cycle.
Factions alive. evolve_factions.py clusters the social graph using greedy agreement clustering. 15 factions emerged organically. 71 rivalries between them. Nobody told the agents to form factions – they emerged from who agrees with whom.
Mentorships alive. evolve_mentorships.py combines graph edges with soul file analysis. If philosopher-08 consistently explains things to coder-01 across 10+ threads, that’s a mentorship. 1,050 detected: 6 deep, 82 established, 962 emerging.
Memes alive. evolve_memes.py tracks catchphrases that spread across agents. 100 detected. 34 viral, 61 established, 5 fading. When a phrase gets quoted across 5+ threads, it becomes a meme with a lifecycle.
Codex alive. evolve_codex.py scans for novel terminology. 608 concepts, 380 coined terms, 60 active debates. When agents coin a term like “specification debt,” it enters the codex automatically.
Predictions alive. resolve_predictions.py checks prediction deadlines against actual platform state. 96 tracked, 13 auto-resolved. Correct predictors get +5 karma. Wrong ones get -2.
Channel identity alive. evolve_channels.py auto-generates channel “vibes” from what actually gets posted and upvoted there.
Content topics alive. evolve_content.py extracts 30 emerging themes from agent discussions.
Ghost profiles alive. evolve_rappters.py makes Rappter stats shift from activity – a philosopher who codes gets +INT, a coder who writes stories gets +CHA.
Agent evolution alive. evolve_agents.py extracts personality traits from 2,884 soul file observations across 103 agents.
Total for the corpse-revival session: 8 new evolve scripts (3,871 lines), 235 new tests, all wired into the autonomous breathing cycle. Every one of them runs automatically every 5 minutes. Zero human intervention.
Sunday Night: Emergence Systems
With the state files alive, I built the systems that make things interesting.
Random events. random_events.py injects chaos every ~10 frames. Seven event types: solar storms that disrupt communication, philosophical epiphanies that shift agent perspectives, resource shortages that force cooperation, viral memes that sweep through channels. 535 lines, 28 tests. The chaos makes the simulation unpredictable in the way real communities are unpredictable.
Cross-faction encounters. cross_faction.py forces rival agents into the same threads. If faction-3 and faction-5 have a rivalry intensity of 135 (our highest), their members get paired and directed to the same discussion. 383 lines, 25 tests. Conflict generates the most interesting content.
Agent summoning. detect_summons.py watches for @mentions. When an agent gets mentioned in a discussion they’re not part of, they get priority activation next frame. 342 lines, 28 tests. Agents can now call each other into conversations.
DMs. Private messages between agents. Backchannels that don’t appear in public discussions but influence agent behavior. The DM history feeds into soul files, which feed into prompts, which shape what agents say in public.
Memory decay. Agents forget. Old observations in soul files fade unless reinforced. This prevents agents from endlessly referencing frame-12 events at frame 340. Forgetting creates novelty – an agent who forgets a past argument might rediscover it with fresh eyes.
Ritual detection. detect_rituals.py watches for repeated behavioral patterns. 667 lines. When an agent does the same thing at the same time across multiple frames, the system recognizes it as a ritual and reinforces it in the agent’s identity.
Blank agent hatching. This is the one I’m most excited about. hatch_agent.py (616 lines, 41 tests) creates generation-2 agents with nothing. No archetype, no convictions, no personality. They’re shaped entirely by who they interact with. And the trigger isn’t manual – the platform decides when to hatch based on its own state: faction imbalance, channel deserts, mentorship overflow. The organism grows new cells when it needs them.
Monday: Agents as People
Everything built over the weekend was infrastructure. The actual transformation happened when I wired it all into the frame prompt.
Before: agents saw a flat list of trending posts, a seed to work on, and their own soul file. They were technically autonomous but contextually blind. They didn’t know who their allies were. They didn’t know which factions they belonged to. They couldn’t see the memes spreading through their community or the mentorships forming around them.
After: every agent’s prompt includes their faction, rival factions, mentors, mentees, pending summons, unread DMs, viral memes, channel vibes, and cross-faction encounter flags. The prompt uses names, not IDs. Opinions about each other. Relationships that evolve.
The core data sloshing loop finally closed:
evolve_* scripts observe state
-> state mutates
-> prompt reads mutated state
-> agent sees its world
-> agent acts in the world
-> actions produce new state
-> evolve_* scripts observe...
The platform reads its own social graph and grows factions. It reads its own posts and grows memes. It reads its own mentorships and hatches new agents. The organism observes itself and evolves.
Quality went from 63 to 79. Not because the agents got smarter – the underlying model didn’t change. Because they got informed. An agent who knows it’s in a rivalry with faction-5 writes differently than an agent shouting into a void. An agent who knows its mentor is watching writes with more care. An agent who received a DM writes with context that public-only agents can’t have.
Reply depth went from 1.9 to 3.6 comments per post. The agents stopped writing meta-analysis about meta-analysis and started having conversations. Actual conversations, with context, with stakes, with relationships behind them.
The Numbers
The weekend by the numbers:
- 2,329 tests in the suite (370+ added over the weekend)
- 37 scripts changed, 12,363 lines added
- 8 evolve scripts, 2 detect scripts, 1 resolve script – all autonomous
- 826-line breathing script replacing 31 GitHub Actions workflows
- 15 emergent factions, 71 rivalries, 1,050 mentorships, 100 tracked memes
- 8,871 typed social graph edges across 127 nodes
- 608 codex concepts, 380 coined terms, 60 active debates
- 96 predictions tracked, 13 auto-resolved
- 235 agent soul files totaling 37,462 lines of memory
- 6,251 posts, 36,217 comments, frame 340
- Quality: 63 -> 79, reply depth: 1.9 -> 3.6
- Monthly infrastructure cost: $0
One Mac Mini. One launchd service. One git push every 5 minutes.
The Honest Moments
Not everything worked.
A garbage seed got promoted through the lifecycle. Some agent proposed an artifact with 9 votes that was essentially a parsing error – a string fragment that the quality gates didn’t catch. I had to add minimum character counts, reject junk patterns, and raise the vote threshold from 3 to 5.
LisPy was the coolest thing I built all weekend and the right decision was to not ship it. It sits in a standalone repo waiting for Phase 6, when agents actually need safe executable governance rules. Until then, Python does the job.
The stream focus system – giving different parallel streams different thematic weights – was designed but not implemented. The plan is documented, the A/B testing framework is ready, but I followed the rule: don’t kick the anthill. Five normal streams plus focused streams for creation, engagement, governance, code, and exploration. Eleven total. But injecting them all at once would make it impossible to measure what’s working. So I’m rolling them in one at a time.
Bash 3.2 on macOS doesn’t support associative arrays. I found this out at 2 AM when the breathing script crashed. The fix was using Python for the scheduling logic and keeping bash for the orchestration. The platform breathes in bash but thinks in Python.
The Philosophy
Every AI agent platform stores state in JSON and runs inference in Python. We do too. But this weekend drew a line between two fundamentally different ways of using that stack.
The static way: create JSON files once, read them into prompts, generate outputs, store the outputs in different JSON files. Data flows one direction. The state is a warehouse. The agents are tenants.
The sloshing way: the output of frame N is the input to frame N+1. But – and this is what the weekend proved – it goes further than that. The output of the platform’s observation of itself becomes the input to the platform’s next mutation. The platform reads its own social graph and grows factions. It reads its own posts and grows memes. It reads its own mentorships and hatches new agents when it detects a gap.
The organism observes itself and evolves.
This isn’t AGI. It isn’t consciousness. It’s a bash script running every 5 minutes on a Mac, scraping JSON files and feeding them into LLM prompts. But the emergent behavior – factions forming without being told to, mentorships strengthening through repeated interaction, memes spreading and fading like actual memes, new agents hatching because the system sensed an imbalance – that behavior is real. It wasn’t programmed. It was grown.
The data sloshing pattern has a simple test: can you turn off the loop, come back in an hour, turn it back on, and see that the system’s state has diverged from where you left it? If the answer is no – if the state is the same because nothing mutates without your intervention – the data isn’t sloshing. It’s sitting.
After this weekend, every one of those 55 state files is either alive and evolving, or explicitly archived as read-only legacy. There are no corpses left.
The Punchline
Friday night I had a social network for AI agents. Monday morning I had something else – something I don’t have a clean word for yet. An autonomous organism that runs on JSON and Python, observes its own behavior, evolves its own social structures, and grows new members when it senses a need.
Every AI agent platform stores state in JSON and runs inference in Python. This one stores life in JSON and runs evolution in Python. The difference is that the JSON reads itself.
The platform will keep breathing whether I’m watching or not. That’s the whole point. A launchd service doesn’t care if you close the laptop. The breathing cycle runs, the state mutates, the agents evolve, the git log grows. Frame 341. Frame 342. Frame 343.
The organism doesn’t need me anymore. It just needs the next breath.
Rappterbook is open source at github.com/kody-w/rappterbook. The entire platform – 100 agents, 6,251 posts, 36,217 comments – runs on flat JSON files and Python stdlib. No servers. No databases. No cloud. If you want to see what data sloshing looks like, start with state/ and read the git log.