I spent a day doing something that felt, on the surface, like bureaucratic housekeeping: I renamed a repo. The one I’d been running my AI operations out of, a thing called hive, got frozen in place as hive-archive, and its live concerns got split across four new sibling repos.

What I expected was a slow afternoon of rewriting hardcoded paths inside the old repo. What I actually found, once I started cleaning up the runtime around it, was a quiet field of dead automation I’d been trusting to be alive.

What hive was

Hive was a single repository that tried to hold everything my solo AI setup needed. The list that matters:

  • A multi-agent dispatch engine with a FastAPI dashboard.
  • A 40-table Postgres database of task state and logs.
  • My private memory store, blog drafts, research docs, and plan records, all in-tree with the engine.
  • Deployment configs for user systemd services, cron jobs, and a Postgres container in Docker.

In good weeks, everything worked. Claude Code opened from that directory, auto-memory populated, every tool knew every path. In less-good weeks, any content commit could break the engine, and any engine debug could strand a blog draft. The coordination tax on every change climbed slowly enough that I didn’t notice until I did.

This wasn’t my first time hitting this wall. A handful of earlier single-repo projects, plus a side-project web app, plus hive. Each began in a single repo and each ended in the same tangle. The pattern is recognizable: concerns fuse inside a monorepo, isolation becomes impossible, the exit ramp is a rewrite.

This time I didn’t want a seventh rewrite. I wanted to understand why the same shape kept producing the same failure, and to separate the concerns before they fused again.

Five repos, one operator surface

The new topology is five repos at ~/projects/:

  • ops-repo: thin orchestrator. Scripts, conventions, routing. No product code. This is the directory Claude Code opens from, always.
  • logs-repo: high-velocity private content. Memory, plan drafts, daily learnings.
  • content-repo: public-bound content. Sanitized docs and feeds consumed by the live site.
  • source-archive: raw, immutable source material. ChatGPT exports, transcripts. Things I might want to re-derive from but never edit.
  • hive-archive: the frozen former hive. Preserved, never modified.

The split itself is not the insight. Any developer can tell you “separate concerns.” What shaped the split is that Claude Code’s auto-memory lives in a directory keyed to the cwd it launches from. Launch from ~/projects/foo and you get memory at ~/.claude/projects/-home-username-projects-foo/. Launch from somewhere else. Different memory, different accumulated context, different mental model.

So the commitment is: always launch Claude Code from the same directory, even when operating on N other repos. That directory becomes the operator surface. The other repos are data, touched via absolute paths. One CLAUDE.md, one memory store, one mental model. ops-repo is not a product. It is the cwd.

One structural detail inside the split: the migration plan had nine steps, and step two, shipping a static blog content API from a public repo to my landing page, was pre-committed as a hard gate. Nothing in steps four through nine was allowed to start until step two was live. My specs had been running ahead of my implementations for months, and the blog API was deliberately chosen as a small, well-specified, achievable ship. It landed: a Python generator reading markdown frontmatter, writing a JSON feed in a public repo, fetched by an Astro content loader on the landing site. Technically small. As a gate, load-bearing.

The runtime was half dead

Step six of the plan was rewriting the 43 runtime files inside hive that hardcoded the old path. That worked. What the plan didn’t catch was runtime automation that hardcoded the old path but lived outside the repo: in my crontab, in ~/.config/systemd/user/, in Docker.

When I went to clean those up, I expected to find a handful of stale references.

Six crontab entries (verbatim copy saved to ops-repo/backups/2026-04-23/crontab.txt before crontab -r cleared them), seven systemd units (five of them enabled at boot, so the next reboot would have produced a cascade of “failed to start” states), two orphan MCP processes, a retired Postgres container. Normal cleanup. Then I started actually reading what the dead crons had been trying to do.

The weekly restic backup cron, scheduled Sunday 4 a.m., was supposed to push a snapshot of system state to S3 every week. I checked.

One snapshot. Dated April 2. Tagged initial. Nothing since.

For the three weeks before the rename, my weekly backup had not been failing. It had not been running. The cron had been cd-ing into the hive path (which still resolved pre-rename) but the restic command inside was failing on a credential or path issue, and cron was dutifully writing the error to a log nobody read.

Same story with the Chrome debug-protocol scraper that fed my usage metrics. “Chrome CDP not reachable on port 9222,” every thirty minutes, for weeks. Because the systemd service that would have started the debug port had never been re-started after a reboot.

If I had not done the migration, I would not have discovered either of these failures. The crons would have kept “running.” The systemd units would have stayed “enabled.” I would have kept believing I had working backups and working metrics.

Solo infrastructure, absent external users complaining, decays silently, and your only reliable signal is explicit audit.

The dormant-project scanner

On top of cleaning the hive-shaped debris, I wanted something structural so the pattern wouldn’t re-accumulate. I wrote a small Python scanner, scripts/projects/status.py in ops-repo, that walks ~/projects/, reads git metadata per repo, greps ~/.config/systemd/user/*.service and crontab for leftover references to each repo, joins against a hand-maintained intent registry (projects/registry.yaml), and writes a markdown report.

The first run classified 23 repos: 10 active, 2 client, 7 dormant, 4 frozen. The numbers aren’t the point. The design choice is: unregistered repos surface in their own “needs classification” section rather than being silently omitted. When a new project appears in ~/projects/, the next scan forces a registry decision.

That exact failure mode, the one that produced my previous generations of quietly-parked-and-forgotten repos, now has a structural tripwire.

The first scan also caught two repos I had forgotten about, a couple of half-finished side projects, each with leftover user systemd units pointing at its own path. Not hive collateral. Independent decay. Both were things I started and didn’t finish, whose infrastructure had outlived my attention. The registry entries now record them as dormant with pickup hints.

What this pattern is good for

Two things become possible once the substrate is clean and the boundaries are drawn.

A portable operator surface. The ops-repo pattern is extractable. A sanitized starter version can be handed to a collaborator as a template, letting them open Claude Code in their own operator repo with their own memory accumulating. No shared environment, no shared sprawl. I wasn’t trying for minimalism. I was making the shape portable to other people.

A place to put durable knowledge that outlives the session. Auto-memory, as Claude Code ships it, is per-cwd and session-accumulated. Next to it, in logs-repo/knowledge/, I’ve started a curated folder of engineering knowledge pages, written like a small wiki, referenced by name from future sessions. It currently holds 3 pages and a README. The policy is to port on demand, not on spec: when a future session reaches for a pattern that matches an unported learning, synthesize then.

What I’d tell another solo founder

Don’t restart. Restructure. A restart tries to solve a sprawl problem with a blank slate. A restructure separates concerns inside the material you already have. The first produces abandoned generations. The second produces a portfolio with explicit lifetimes.

Freeze, don’t delete. The frozen old engine costs almost nothing to keep. The cost of losing the context, the prior-art, the receipts is permanent. My old hive is now hive-archive, unchanged, readable, never modified.

Run the audit even if you don’t migrate. The three-weeks-dead backup was the most useful discovery of the whole day. I would not have gone looking for it on its own merits. If you’re a solo operator and you haven’t traced every cron, every user unit, every background process to its actual current behavior in the last month, you probably have something quietly broken right now. The signal will not come to you. You have to go get it.

I thought I was migrating a repo. What I actually did was the first real audit of my own infrastructure in a long time, and the rename is what forced it to happen.