Why I built Neutron
OpenClaw failed me as a daily driver. Six weeks of compliance gaps in my corrections log convinced me to rebuild the harness from scratch. Neutron is the result: an open-source, self-hosted harness for orchestrating long-lived Claude Code sessions, with the rules in code rather than convention. It runs on your own machine, on your own Claude subscription. I run mine through Telegram all day; you don't have to.
Early April 2026. I am six weeks into running three companies and basically all of my personal life with OpenClaw, Peter Steinberger’s open-source agent framework. I keep a corrections log. The corrections log is six pages long. Same pattern over and over: agent skips its own startup protocol, agent doesn’t read the file it just wrote, agent claims a step it didn’t take, agent re-researches a task it finished yesterday. The framework is brilliant. The cumulative result of using it day-to-day is that I spend the majority of my agent-time fixing the agent. I am running out of patience.
I shut OpenClaw down on 2026-04-08 and started building my own thing. The first commit in the repo is timestamped 2026-04-08, message Initial Vajra setup: gateway, prompts, config. Forty days and 438 commits later the system is what I use for everything: every business decision, every reminder, every line of code Forge ships, every research dispatch, the very production of this post.
I call the private installation Vajra. The open-source harness I extracted from it is Neutron, with the source at github.com/rjunee/neutron. The category, as the industry has converged on it through 2026, is the agent harness: the runtime, memory, tools, scheduling, and policy layer wrapped around the model. Frontier models are necessary; they are not sufficient. The scaffolding around them is the product. Neutron is open source (Apache 2.0) and self-hosted. It runs on your own machine, your data stays on your own disk, and it talks to the model through your own Claude subscription. Nothing leaves your machine except the model calls you authorize. This post is about why I built it, what it actually is, and how it works underneath. With file paths, real numbers, and the architectural choices that turned out to matter.
Part 1: What broke for me with OpenClaw
OpenClaw is Peter Steinberger’s framework for running Claude as a daily-driver coding agent. It is the closest direct ancestor of my system. The conventions I still use (a SOUL file for personality, a USER file for the operator, a TOOLS file for tool inventory, a MEMORY index for durable facts) are all OpenClaw’s. The structural idea, that a long-running agent needs durable files that any session can read into its system prompt, is OpenClaw’s. Credit where it is due. The repo is at github.com/openclaw/openclaw.
What broke for me was not the design. It was the daily-driver experience. Two weeks of February and three of March, side by side with my own corrections log at ~/vajra/Resources/system/corrections-log.md, show the shape.
2026-02-19. Asked the agent for a financial document. It searched two of the five PARA folders, didn’t find it, asked me where it was. The doc was at Resources/system/financial-documents.md. I had recorded it. I had told the agent. The startup-protocol rule that says “search all five locations” was in the AGENTS.md file. The agent had read AGENTS.md that morning. It didn’t comply.
2026-02-23. Switched from the General topic to RoboBuddha for a Phase 0 walkthrough. Asked “give me the first instruction.” Agent had zero context and asked me what I was talking about. Same class as the Feb 19 failure. AGENTS.md still had a Project-Start protocol; the agent still skipped it. I logged this as the fourth recurrence and noted “this is an LLM compliance gap, not a missing rule.”
2026-02-23. Wave 3a parallel-agent execution: four failed launches before one succeeded. Attempt 1 failed on un-authed Claude CLI. Attempt 2 failed on the wrong execution model. Attempt 3 failed on un-authed Codex. Attempt 4 failed on missing node_modules in worktrees. Only the fifth attempt produced code, and I still had to manually commit and merge. No pre-flight checklist existed. Spawn helpers logged success at fork-time and never asserted the child reached useful state.
2026-02-23. Agents writing AS-BUILT.md entries that claimed steps they never took. The agent said it ran drizzle-kit push when there were no node_modules on the worktree it had used. Logged as a permanent rule: “never trust agent self-reports for docs.”
2026-02-24. Relayed an outdoor lighting smart-switch recommendation from a sub-agent to me in chat. Did not write it to a file. Session expired. The recommendation evaporated. I had to forward my own old message back to the agent the next day. Sixth occurrence in the same recurring class: information exists only in ephemeral context, never in durable files.
2026-02-28. Tabs label-review approved by me on Telegram. Agent wrote it to the daily note. Did NOT update the project README frontmatter. The dashboard regenerated from stale frontmatter. After compaction, the agent saw two contradictory states (daily-note says DONE, dashboard says PENDING). Picked the dashboard. Surfaced labels as an open action item again. Seventh occurrence of write-through failure.
2026-03-04. Asked for permission to dispatch the next wave of parallel agents. The plan was approved. The first three tasks landed clean. Zero blockers existed. The agent still asked.
Read those one after another. The shapes repeat. Compliance gaps where the rule was written but the agent skipped it. Write-through failures where state existed in one file and was contradicted in another. Spawn helpers that logged success without verifying the child came up. Permission-asking on already-approved work.
None of these are OpenClaw bugs in the strict sense. They are the consequence of a framework that asks the model to comply with written rules at every decision point, with no mechanical enforcement underneath. The rules accumulate. The model’s compliance with rules N+1 degrades as N grows. There is no Stop hook that refuses to let a turn end without writing to a file. There is no enforce-reply guard that catches the agent printing terminal output that the user can’t see. There is no spawn-helper assertion that the child is actually alive before logging success. The framework is a set of conventions. Conventions don’t survive social or attentional pressure on the model. They survive in code.
I had a 336-line corrections log after six weeks. The 7th entry of write-through failure was the moment I decided to rebuild from scratch.
Part 2: The model question
Before rebuilding I tried the obvious alternative: drop OpenClaw, switch to a different model or a different CLI. By March 2026 the multi-model agent-tooling ecosystem was big enough that I had real options. I ran each of them against the actual work I needed done: code-shipping into the Vajra repo, multi-step research synthesis, dispatching reminders that pulled real context, browsing my Gmail and Calendar through MCPs.
Cursor’s CLI mode is good at code in the editor. Pulled out of the editor, it lost the agent-loop properties I needed. Aider is a precise code-editing loop with first-class diff handling. It is not an agent shell. No tool ecosystem beyond what the wrapper code provides, no MCP, no skills, no sub-agent dispatch primitive. Gemini’s CLI was new and rough; the model itself was capable, but the surrounding harness (skills, MCP, tool conventions, hooks) had a fraction of Claude Code’s depth. OpenAI’s codex CLI was strong on code generation as a one-shot but did not have the long-running multi-tool conversational shape I needed for daily-driver work. The locally-hostable agents (Open Interpreter, autoGPT-shaped tooling, the various LangGraph variants) were structurally a long way behind on the tool ecosystem and hooks that I now consider load-bearing.
The thing that turned out to matter was not just the model. It was the surrounding harness. Claude Code is a model plus a real tool ecosystem plus MCP plus skills plus hooks plus a sub-agent dispatch primitive plus the --print headless mode plus the --resume session-persistence shape plus the Agent SDK. The total of those, in March 2026, was substantially ahead of any other CLI agent. Other models could match Claude on a specific narrow benchmark. None of them came with the harness depth.
So I made a bet. Claude Code is the only LLM runtime good enough to base a daily-driver agent on. I will build the harness around it, not around a model abstraction.
This is contrarian in the multi-model-orchestration era. Most agent frameworks try to be model-agnostic. They wrap a provider interface, expose a model-selector flag, and let you swap backends. Neutron does the opposite. Every topic agent is a long-lived Claude Code session. Every background worker is a fresh, short-lived Claude Code process. The reminder agent at fire time is a Haiku-4.5 spawn. The gateway is a TypeScript service whose only job is to route messages between a chat interface and those processes. (The harness boundary is clean enough that you could point the substrate at local open-weights for an airgapped install, but my bet, and the one I keep making, is Claude Code.)
The tradeoff is real. I am fully exposed to Anthropic’s product decisions. A breaking change to --resume, a regression in MCP, a CLI flag deprecation, and my system is broken until I patch. I have caught one of each in production (see the prior post on failure modes) and I pay the cost because the cost of being model-agnostic is harness shallowness, which is the thing that broke OpenClaw for me.
One concrete consequence: my interactive Claude usage is well past what a single Claude subscription covers. I bought a second one in early May. The marginal cost of the second subscription is dwarfed by what the system produces. The substrate has shipped 271 PRs autonomously across two repos via Trident, written this post via an Atlas dispatch, classified 305 scribe extractions into the entities layer, and posted 70 active reminders. Two subscriptions of real money is the right price.
Part 3: Neutron in one sentence
Here is the whole system, compressed.
Neutron is a self-hosted harness that runs a fleet of long-lived Claude Code sessions on your own machine, one per project, and gives you one place to talk to all of them.
That is the load-bearing claim. Everything else is implementation.
The interface ships two ways out of the box: a bundled web app and a mobile app. That is the default for anyone running Neutron. I personally run mine through Telegram, because it is already on my phone, my laptop, and my watch, and I am in it all day anyway. Telegram is an optional channel adapter, not a requirement. The rest of this section describes my Telegram setup because it is the one I actually live in, but nothing about the architecture depends on it.
My phone never runs Neutron. My phone runs a chat client. My Mac runs Neutron. The architecture is local-first, mobile-presented. From a UX standpoint the chat client (Telegram, the way I run it) gives me forum topics for project separation, voice messages for hands-free dispatch, inline keyboards for approval workflows, and native search across history. From an architecture standpoint the chat client is the transport layer. Inbound messages route through the channel adapter to the gateway on 127.0.0.1:7777. Outbound messages from any agent in the fleet go back through the adapter and land in the same topic the inbound came from. The phone is a UI; the Mac is the substrate.
The two-process model:
flowchart LR
Phone[Phone or laptop<br/>Telegram client]
Bot[Telegram Bot API]
GW[Gateway<br/>Bun HTTP server<br/>127.0.0.1:7777]
T1[Topic CC: Vajra<br/>claude -p --resume<br/>tmux pane 18816]
T2[Topic CC: Pristine<br/>claude -p --resume<br/>tmux pane 1624]
T3[Topic CC: Neutron<br/>claude -p --resume<br/>tmux pane 6824]
SA[Sub-agents<br/>forge / atlas / sentinel<br/>argus / scribe / reminder]
Phone <--> Bot
Bot <-->|webhook + sendMessage| GW
GW <-->|stdin / stdout via tmux| T1
GW <-->|stdin / stdout via tmux| T2
GW <-->|stdin / stdout via tmux| T3
T1 -.->|spawn-agent.sh| SA
T2 -.->|spawn-agent.sh| SA
T3 -.->|spawn-agent.sh| SA
SA -->|tg-post.sh -> /post| GW
The dashed lines from the topic CCs to the sub-agent fleet are the dispatch primitive: a topic agent calls spawn-agent.sh with an agent name, slug, target chat and thread, and a task prompt. Sub-agents run in their own claude -p child processes, do work, post their results back to the originating topic, and exit. We get to those in Part 6.
Part 4: Topic-to-session mapping
One Telegram forum topic. One persistent Claude Code process. One STATUS.md, one PROMPT.md, one set of isolated context.
The routing table is ~/vajra/gateway/topic-map.json. A real entry, lifted verbatim:
{
"389": {
"thread_id": "389",
"name": "vajra",
"session_name": "vajra-vajra",
"port": 18816,
"type": "project",
"project_path": "/Users/ryan/vajra/Projects/vajra",
"display_name": "Vajra",
"cwd": "/Users/ryan/vajra/Projects/vajra",
"prompt_file": "/Users/ryan/vajra/Projects/vajra/PROMPT.md",
"has_session": true,
"session_id": "bbb33242-c7e8-465a-af93-07aaaaa06506",
"cc_starttime": "Sun May 17 19:44:18 2026",
"pid": 15524
}
}
The key is the Telegram message_thread_id as the gateway sees it on inbound webhooks. The value carries everything needed to route a message into the right Claude Code process: cwd for the working directory, prompt_file for the topic’s personality layer, session_id for claude --resume, port as the MCP reply tool’s loopback port, pid for liveness checks, cc_starttime for orphan detection.
The map has 22 active topics at the time of writing, plus a general default:
389 vajra 393 amascence 409 email-system 893 vps-migration
390 robobuddha 394 book 462 cc-website 1121 relationship
391 lumina 395 biohacking 1237 sound-ceremony 1624 pristine
392 business-monk 396 alchemy 2507 cc 2828 tabs
4166 tax 5200 quintessential-ventures 6824 neutron
8823 neutron-coding 14620 btx 16145 minas-tirith general
Each row is one Telegram topic and one Claude Code process. Tabs is a single conversation that has been running, with periodic --resume rehydration, since 2026-04-08. Pristine is a different one. Neutron is a third. They share my Mac, my filesystem, and the gateway. They share nothing else. Switching topics in Telegram is, at the substrate level, switching to a different long-lived Claude Code process with a different working directory and a different STATUS.md and a different conversation history. Concerns don’t bleed.
The topic-isolation property is the thing OpenClaw didn’t give me. In OpenClaw the “agent” was one assistant identity that drove every conversation, and every cross-project conversation reused the same memory store, the same recent-context window, the same compaction profile. Project boundaries were folklore. In Neutron, project boundaries are processes. A Tabs conversation cannot accidentally cite a Pristine decision because the Tabs Claude Code session has never seen the Pristine STATUS.md. If I need cross-topic context I dispatch a sub-agent that explicitly reads what it needs and reports back. The default is isolation; cross-topic context is opt-in and explicit.
The reply tool
Every topic Claude Code process runs in a headless tmux pane. There is no human looking at the pane. Terminal output is invisible to the Telegram user. The only way back to Telegram from a topic agent is through the topic’s MCP server, which exposes a single tool named reply.
The base prompt every topic CC inherits, at ~/vajra/prompts/topic-agent-base.md, says it bluntly:
CRITICAL: You MUST use the `reply` tool for ALL responses to channel
messages. Text you output to the terminal is NOT visible to the user.
You are running in a headless tmux pane. Only the `reply` tool sends
messages to Telegram. Every channel message MUST get a `reply()` tool
call with the chat_id and message_thread_id from the message meta.
Never just print text to the terminal. Always call `reply`.
That is a written rule. The OpenClaw lesson is that written rules erode under attentional pressure on the model. So the rule is also mechanically enforced. A Claude Code Stop hook at ~/vajra/gateway/hooks/enforce-reply.ts runs on every turn-end attempt, walks the transcript backward to find the last user-originated turn, checks whether it was a channel message, and if it was, checks whether the assistant called reply (or any MCP-wrapped tool whose name ends in __reply) during the turn. If not, the hook emits {"decision": "block", "reason": "..."} and the agent’s turn refuses to end.
The hook is 200 lines. It logs every block to ~/vajra/gateway/enforce-reply.log. It exempts notice turns (informational context posted by other agents) so the agent doesn’t get blocked on context it shouldn’t reply to. It has its own incident history: on 2026-04-15 the hook was failing to match the MCP-wrapped tool name (mcp__vajra-vajra-channel__reply) because it was checking name === 'reply' instead of name.endsWith('__reply'). The fix is in the file with a dated comment. The OpenClaw conversational-rule failure mode is precisely the thing this hook closes.
Part 5: The gateway
The gateway is a single Bun HTTP server at ~/vajra/gateway/index.ts, 9649 lines, hostname 127.0.0.1, port 7777. It is the central nervous system. Routes I care about:
GET /health: per-topic alive checks plus event-loop metrics, scraped by an external watchdog.POST /reply: the topic CCs’ way back to Telegram (via the MCP reply tool).POST /post: ad-hoc cross-topic post, used bytg-post.shand every sub-agent’s completion message.POST /post-reminder: reminder-agent fire path, attaches inline keyboard (Snooze, Disable) and tracks single-use callback tokens.POST /reminders+GET /reminders/...: CRUD over the durable reminder store atgateway/reminders.json.POST /admin/restart: chat-safe gateway restart. Spawns a detached restarter so the impending death of the current gateway can’t kill the restarter (the script-based path SIGKILLs every descendant including the topic CC that asked for the restart, which is a regression I caught and replaced with this endpoint).POST /admin/respawn-topic: context-preserving topic-CC respawn that reuses the session ID via--resume.POST /forge/delivered,POST /argus/delivered: agent-completion endpoints that wire up the dispatch buttons in the originating topic.POST /scribe/spawn: fire-and-forget extraction trigger for the auto-write-to-entities pipeline.POST /typing,POST /nudge/tick,POST /create-topic,POST /attach,POST /dispatch: the rest of the operational surface.
Underneath the HTTP routes, the gateway runs a stack of setInterval-driven supervision loops. Each one is a distinct watchdog imported from its own module:
watchdog: every 15 s, readsrunning-agents.jsonl, detects crashed and stuck sub-agents, posts alerts back to the originating topic.scribe-watchdog: reaps scribe children that run past 300 s.session-size-watchdog: monitors session JSONLs for the size threshold that triggers/compact.model-update-watchdog: watches the auto-update channel for Claude Code regressions.pane-scan-watchdog: periodic tmux pane health probes.heartbeat-watchdog: gateway-itself self-heartbeat into the event-loop monitor.banner-check-watchdog: catches version-banner drift across topic panes.stuck-turn-watchdog: surfaces topic CCs whose last turn hasn’t progressed past the threshold; the cooldown state lives in.stuck-turn-state.json.cwd-drift-watchdog: alerts when a topic CC’s working directory mismatches itstopic-map.jsonentry.login-watchdog: catches Claude Code subscription auth lapses.
Each watchdog is independently testable and independently failable. Several of them exist because of specific OpenClaw-era failures: the stuck-turn watchdog because OpenClaw agents would hang silently mid-turn with no surface signal; the cwd-drift watchdog because OpenClaw’s session-resume sometimes restored a session into the wrong directory and the next file-write landed in the wrong place; the heartbeat watchdog because OpenClaw could lock up its own event loop and have no external trip to notice.
The inbound message flow, end to end:
sequenceDiagram
participant P as Phone (Telegram)
participant Bot as Telegram Bot API
participant GW as Gateway (Bun.serve)
participant TMap as topic-map.json
participant Pane as tmux pane (topic CC)
participant CC as Claude Code process
P->>Bot: user types message in forum topic
Bot->>GW: POST /webhook { chat_id, message_thread_id, text }
GW->>TMap: lookup by message_thread_id
TMap-->>GW: { name, port, session_id, cwd, pid }
GW->>Pane: tmux send-keys (inject <channel> turn to stdin)
Pane->>CC: stdin event arrives mid-loop
CC->>CC: read, plan, run tools, write reply
CC->>GW: POST /reply (via MCP reply tool on loopback port)
GW->>Bot: sendMessage (chat_id, message_thread_id, text)
Bot->>P: outbound message appears in topic
A few details that turn out to matter. The tmux send-keys path writes the inbound message into the topic CC’s stdin in real time. There is no intermediate queue: the topic CC is a long-running interactive Claude Code session, the gateway is talking to it the way a human types into a terminal. The reply tool path is HTTP-loopback rather than tmux-stdin because we need a structured payload (chat_id, thread_id, optional streaming flag) and parsing that out of pane output would be fragile. The session_id in the routing record is the Claude Code session UUID that --resume needs; the gateway uses it on every topic-CC respawn so context survives crashes.
Part 6: The sub-agent fleet
A topic CC’s day-to-day work is mostly conversation. When it needs to do something heavier (build code, run a multi-step research dispatch, review a PR, transcribe and classify a long voice message), it spawns a sub-agent. The script is ~/vajra/scripts/spawn-agent.sh, 410 lines, the only blessed dispatch path.
Signature:
scripts/spawn-agent.sh <agent> <task-slug> <chat_id> <thread_id> \
-- "<task-prompt>"
# agent ∈ {forge, atlas, sentinel, argus, scribe}
What it does, line by line:
- Generates an
agent_idof the form<agent>-<slug>-<timestamp>-<random>. - Resolves the prompt file by agent type (
prompts/forge.md,prompts/atlas.md, etc.). - Builds the
claude -pinvocation with the appropriate flags: `—dangerously-skip-permissions —verbose —output-format stream-json —append-system-prompt-file
. The scribe variant uses —bareplus—system-prompt-filefor a hardened isolated context (no CLAUDE.md auto-discovery, no plugin sync, no Vajra cascade). 4. Launches the child as a fully-detached background process, redirecting stdio togateway/agent-logs/<agent_id>.jsonl. 5. Writes a spawnrow togateway/running-agents.jsonlwith the real child PID (not the wrapper subshell PID), holding aflockon.registry.lockto prevent contention with the gateway's atomic registry-prune. 6. Echoes theagent_idto stdout so the caller can track it. 7. On child exit, appends acompletedrow to the same registry with the exit code. <p> The agent then does its work entirely on its own. When it finishes, it posts its result to Telegram viatg-post.sh, which calls the gateway's POST /postendpoint. The post lands in the originating topic AND the gateway echoes it as asystem=“notice”` channel turn into the next inbound for the topic CC that spawned it, so the topic CC has the result in its own context the next time the user replies.
sequenceDiagram
participant TC as Topic CC
participant SA as spawn-agent.sh
participant Reg as running-agents.jsonl
participant Sub as claude -p (sub-agent)
participant TG as tg-post.sh
participant GW as Gateway /post
participant Bot as Bot API
participant Topic as Telegram topic
TC->>SA: spawn-agent.sh forge plan-it 0 389 -- "build X"
SA->>Sub: claude -p --append-system-prompt-file forge.md ...
SA->>Reg: append { event: spawn, agent_id, pid, ... }
SA-->>TC: prints agent_id, exits
Sub->>Sub: runs to completion (minutes to hours)
Sub->>TG: tg-post.sh CHAT THREAD "✅ Forge done: ..."
TG->>GW: POST /post
GW->>Bot: sendMessage
Bot->>Topic: result visible to user
GW-->>TC: echo as <channel system="notice"> on next turn
Sub->>Reg: append { event: completed, agent_id, exit_code }
The fleet has five named roles, all backed by their own prompt file in ~/vajra/prompts/:
- Forge. Code-shipping agent. Takes a sprint brief, runs
/slfg(the spec-led-Forge-go skill), writes code in a worktree, runs tests, opens a PR. The build half of Trident. - Atlas. Research, writing, synthesis. The agent that wrote this post is an Atlas dispatch.
- Sentinel. Review for non-code work (research outputs, decision frameworks, marketing copy). Forge’s sibling on the writing side.
- Argus. Code review. Reads PR diffs, runs a cross-model verdict via
codex-review.sh, synthesizesAPPROVEorREQUEST CHANGES. The review half of Trident. - Scribe. The auto-extraction agent that watches Telegram for messages worth preserving, transcribes voice via
whisper-cli, runs a Haiku-4.5 classification pass, and writes timeline entries or originals to~/vajra/entities/. Detailed mechanics in the memory architecture post.
A sixth role, the reminder agent, is not spawned through spawn-agent.sh. It runs on the gateway’s reminder tick. Mechanics next.
The reminder loop
Reminders live in ~/vajra/gateway/reminders.json. 70 active entries. Each entry has a cron-style schedule (or a one-time fire-at), an enabled flag, a target chat and thread, and a message body that can be a literal string, a smart-agent instruction, or a pattern-template reference.
The gateway’s reminder tick scans the file. For every reminder whose next_fire has elapsed and whose enabled is true, it spawns a one-shot claude -p process with ~/vajra/prompts/reminder-agent-base.md as the appended system prompt and the reminder’s message body as the user prompt. The agent uses Haiku 4.5 (the gateway’s FAST_MODEL resolved from gateway/models.ts) for cost reasons. It composes a single context-rich message (pulls weather, calendar, project state, time-of-day if relevant) and posts it via POST /post-reminder, which attaches a [Snooze 1h] [Disable] inline keyboard with single-use callback tokens.
sequenceDiagram
participant T as Reminder tick (setInterval)
participant R as reminders.json
participant GW as Gateway
participant Sub as claude -p (Haiku 4.5)
participant Tg as tg-post or /post-reminder
participant Topic as Telegram topic
T->>R: scan for due + enabled
R-->>T: due[]
loop for each due reminder
T->>Sub: claude -p --model haiku-4-5 \
--append-system-prompt-file reminder-agent-base.md \
-- "<message body>"
Sub->>Sub: pull context (weather, calendar, STATUS.md)
Sub->>Tg: POST /post-reminder { text, keyboard }
Tg->>Topic: outbound nudge with Snooze + Disable
T->>R: advance next_fire (cron) or disable (one-shot)
end
The shape is the same as the rest of the fleet: a long-lived TypeScript supervisor spawns short-lived Claude processes that do one thing and exit. Persistence lives in JSON files. Coordination lives in the supervisor’s tick. No queue server, no message broker, no orchestration framework.
Part 7: The numbers
Forty days of production. Real counts from disk at the moment of writing.
- 22 active topics in
topic-map.json, plus ageneraldefault. 22 long-running Claude Code processes. - 438 commits to the Vajra repo since the first one on
2026-04-08. 24 of them arefix:commits. - 1,251 lifetime events in
running-agents.jsonl: every sub-agent spawn, completion, crash, and watchdog alert. - 614 sub-agent spawns across all five roles. Breakdown by agent: scribe 305, forge 159, argus 118, atlas 29, sentinel 3.
- 572 completions, 29 crashes, 10 stuck-kills, 6 fallback-alerts, 20 heartbeat-alerts. The crash + stuck rate is real and is the reason every watchdog above exists.
- 70 active reminders in
reminders.json. - 9,649 lines in
gateway/index.ts. 410 lines inspawn-agent.sh. 200 lines inenforce-reply.ts. The gateway is the bulk of the substrate; the wrappers around it are small. - Two Claude subscriptions in flight. The second one paid for itself in a week of Trident sprints.
Substantially more interesting than any single number is the shape of the registry. 614 spawn events. 572 completions. The arithmetic gap (42 events) is what the watchdog catches: 29 crashed agents, 10 stuck-killed, 6 fallback-alerted, plus the 14 currently in-flight at the moment of the count. Every gap row triggered a Telegram alert into the originating topic. None of the gap rows produced silent state. The substrate fails loud or it doesn’t fail.
Part 8: What it gives me
Every morning I open Telegram and the brief is already in my General topic. The brief was composed by an Atlas spawn at 06:00 that read overnight email, my calendar, the morning weather, the STATUS.md of every active project, and the open items in ISSUES.md. The brief tells me what is on me today, who is waiting on me, and what is at risk if I do nothing. Five years ago I would have spent forty-five minutes on that work. I read the brief in three.
When I want code shipped, I dispatch a Trident run. The build, the cross-model review, the fix loop, and the squash-merge all happen without me. I see every state transition on Telegram in real time. I can stop the run with /trident stop <slug> at any phase. 271 PRs landed this way across two repos. The detail is in the prior post.
When I want research, I dispatch an Atlas spawn. This post is one of them. Atlas reads the briefing, gathers from the corrections log, the gateway code, the topic map, the registry, writes the draft, validates the build, posts the result. I review the draft and ship it.
When I think out loud on Telegram, scribe extracts the framing into entities/originals/<date>-<slug>.md within seconds, with the verbatim block preserved, plus a timeline entry on the relevant person or company page. 305 scribe spawns lifetime. My externalized thinking compounds into a wiki layer that I can read on my phone or query from any topic via QMD.
Reminders fire with context. The 10am “go feed the dogs” nudge knows the weather, knows the current STATUS of the project I was working on, knows whether I am at home or in a calendar block. The 6pm Tabs PnL review knows what changed in the last 24 hours. The reminder is not a string; it’s a one-shot Haiku spawn composing a personalized message at fire time.
All of that runs on top of one substrate. The substrate is Claude Code processes, a gateway that knows how to route to them, a sub-agent dispatch primitive, a watchdog stack, a reminder loop, and a Telegram client on my phone. The total code is well under 20,000 lines. None of it depends on a model abstraction; all of it is exposed to Anthropic’s product decisions, which I accept.
What the system gives me back is the part of the day that compounds. It doesn’t make my decisions for me. It doesn’t write my book for me. It doesn’t replace my judgment on hires, on capital allocation, on negotiations, on the call from a customer at 7pm. What it does is take the part of the work that was eating my hours and didn’t need to be (the routing, the dispatching, the synthesizing, the remembering, the typing) and hands me back the part that actually wants my attention.
OpenClaw failed me because the agent skipped the rules. Neutron works because the rules are in code, the substrate is the right one, and the human gets to do the human work.
Neutron is open source at github.com/rjunee/neutron, Apache 2.0. If you want the full teardown of how the pieces fit together, build your own walks the architecture from a single tmux pane up to the whole fleet. If you want the announce and the self-host path, Neutron is open source covers what is in the repo.
I'm productizing this substrate as Neutron for operators who want a real agent system without rebuilding it themselves. Separately I take on a small number of consulting engagements per quarter for teams shipping into production. Services →