← All articles

The Agentic Harness: What It Actually Takes to Run a Monorepo Through an AI Agent

People ask me some version of the same question when they hear how much of Singularity — the monorepo underneath most of what I've been writing about this year — gets built through Claude Code rather than typed by hand: "so the AI just... writes it?" The honest answer is no, and the interesting part isn't the AI. It's everything I had to build around it before that arrangement became something I could actually trust across a project this size, run over hundreds of sessions, months apart, by an agent with no memory of yesterday unless I gave it one.

I've come to think of that scaffolding as a harness. Not in the "guardrails that stop the AI from doing something bad" sense — I have some of those too — but in the older sense of a harness on a draft animal: a structure that converts raw capability into directed, repeatable work. Claude Code is genuinely capable. Left alone across a 20-plus-bounded-context Nx monorepo with no procedure, it is also genuinely unreliable in exactly the ways you'd expect from something that starts every session with zero context: it forgets decisions, re-litigates settled architecture, drifts a planning doc out of sync with the code three sessions after a hand-off. None of that is a model problem. It's a structure problem, and it's solvable.

The Problem Nobody Warns You About: Zero Memory, Every Session

Here's the thing that surprised me most going into this. The actual coding was never the hard part — Claude Code is good at CQRS handlers and Angular components. The hard part was that every session starts from nothing. There's no persistent "the AI remembers this codebase" the way a human contractor accumulates context over months on a project. Each session is, in a real sense, a new hire who read the whole codebase overnight and forgot everything about the conversation we had yesterday.

That's the actual constraint the entire harness is built to solve. If I can't fix the memory problem at the model layer, I have to fix it at the process layer: write down what a fresh session needs to know, in a place a fresh session is required to look, in a format cheap enough to keep current every single time. That's CLAUDE.md, and it's grown into something closer to an operating manual than a README — architecture guide, bounded-context tags, Session Start and Session Stop procedures, a PR checklist, per-step commit discipline, all in one file every session reads before doing anything else.

Session Start / Session Stop: The Bookends

The two procedures that matter most are the ones that run at the boundary of every working session — Session Start and Session Stop, both defined in CLAUDE.md.

Session Start is nine steps: load a persistent memory file, check docs/planning/active-projects.md for what's actually in flight, run git status and git log, find the phase doc for the current branch, read the phase summary and checklist, scan the parking lots and session notes for anything left over from last time, run a docs-consistency check across everything just read, brief me in 3–5 bullets, and only then — if needed — cut a new phase branch. Session Stop is the mirror image: update the checklist, update the phase summary with any new decisions, update the feature doc's phase status, run the docs-consistency check again, update active-projects.md, lint/test/build the active project scope, write any hard-won lessons to the knowledge base, update memory, commit, push, and open the phase PR if the checklist is actually done.

Reading that as a list makes it sound like ceremony. In practice it's the only reason a session that starts cold can be productive inside the first ten minutes instead of the first hour spent re-deriving what I already decided last time. docs/planning/active-projects.md right now tracks five actively "in flight" efforts at once — Developer Portal on a sprint/story/example branch model, the Lean-Agile MVP Skills work, the docs-store CouchDB+TypeDB spike, the new docs-store-api Platform API scoping, plus one blocked item — and every one of them has to be resumable by a session that has never seen it before. That's not a nice-to-have. Without the active-projects table as a single source of truth, I genuinely could not keep five concurrent long-running efforts straight across sessions that are sometimes weeks apart.

Docs as the Actual Source of Truth, Not a Record of One

The thing I underestimated when I started working this way is how fast planning docs drift from each other once an AI agent is producing most of them. A feature doc says a phase is "in progress" three sessions after it actually merged. A workshop introduces a renamed term that never propagates into the doc written the day before. A sprint schedule gets duplicated into a second doc and the two quietly diverge.

That drift is what check-docs-consistency exists to catch — a skill, not a one-off review, run at every Session Start and Session Stop rather than as a periodic sweep. The algorithm is deliberately narrow: extract every checkable claim from a downstream doc (a count, a status word, a piece of vocabulary a workshop coined), verify it against the specific upstream doc it depends on, and classify any mismatch as a stale claim, an un-propagated decision, an append-not-merge duplication, or an internal contradiction. It was extracted after running that exact check by hand four times on one project and finding a real defect every single time. Running it at every session boundary is what lets this monorepo skip an expensive, periodic, workspace-wide consistency sweep — drift gets caught at the boundary where it's cheapest to fix, one project's docs at a time, instead of accumulating into something that needs its own dedicated remediation phase later.

Role Personas: Getting the Right Judgment Applied to the Right Work

One thing I got wrong early on: I had written seven detailed, research-grounded role prompts — Product Owner, UX Designer, Architect, SRE/DevOps, QA Tester, UAT Tester, Developer — as reference documentation under agents/roles/, and then did the work under the default assistant persona anyway, because nothing in the tooling actually nudged anyone toward using them. The fix was to add tooling support for project-scoped subagents, letting me create thin-pointer wrappers for each role. This is a good example of the harness correcting itself mid-project rather than me writing a perfect process up front. Claude Code supports project-scoped subagents via .claude/agents/*.md, so each role file got a thin-pointer wrapper — not a duplicate of the research, just enough to summarize the identity and non-negotiables and then send the agent to read the source file in full for anything non-trivial.

The rule for when to actually invoke one is deliberately narrow: use a role subagent when the task's primary success criterion matches that role's key question — for the UX Designer, "can the user find and use this capability without help?" Routine work that happens to touch a UI file doesn't need it. Design judgment as the actual point of the task does. This piece you're reading right now, for what it's worth, was drafted by the content-writer persona, grounded in agents/content/voice_system_prompt.md — one more instance of the same pattern applied to non-code work.

Procedure Discipline: Commits, Branches, and the Cost of Skipping Either

A few of the harness's rules exist purely to make a long AI-driven history legible after the fact. Per-step commit discipline means every phase step produces exactly one commit before the next step begins — no batching three checklist items into one commit because it felt faster in the moment. The phase-branch model requires every implementation commit to land on a phase/<feature>/<N>-<slug> branch cut from the feature branch, never directly on main or the feature branch itself. Defect resolution follows a test-first discipline in a very literal sense: write the failing test, commit it alone so there's a permanent red-state record in git, then fix it in a separate commit.

None of these are abstractly "good practice" bullet points I copied from somewhere. Each one exists because skipping it once caused a real, specific problem that showed up later and was expensive to untangle — which is exactly what the knowledge base is for.

The Knowledge Base: Lessons That Would Otherwise Repeat

docs/knowledge-base/lessons-learned.md is, as of this writing, over 7,600 lines, and I don't think of that length as bloat — it's the actual cost of not re-learning the same trap twice. A recent entry: copying a workshop-authored Gherkin Feature verbatim into a real Cucumber suite hit three separate failures before the first Scenario would even run — a soft-wrapped Then step that Gherkin's parser reads as two malformed lines instead of one wrapped sentence, a Cucumber Expression {string} silently failing to match an unquoted identifier (reported as "Undefined," not an error), and a regex step definition throwing an arity mismatch because a capture group had no matching function parameter. Every one of those three failures is either silent or mislabeled by default. None of them look like what's actually wrong until you've hit it once and written it down.

Another one, from designing the Sprint/Story/Example branch lifecycle itself: a text-only version of a process that had originally been designed as a diagram quietly dropped an entire branch — the perpetual dev branch every merged Example needs for continuous integration and third-party webhook connectivity a local workstation doesn't have. Nothing about the text procedure was internally inconsistent. The diagram just encoded a parallel branch a linear list of steps doesn't naturally represent, and the gap was invisible until I supplied the original reference diagram and noticed the row was missing. The general lesson I took from that one: when a process was originally designed as a diagram, ask for the diagram before finalizing an ADR from the prose description alone. A process description in words is a lossy re-encoding of the original design, and the losses are exactly the kind of thing that look complete until someone who remembers the original notices what isn't there.

I'd be lying if I said every lesson in that file represents an hour saved later. Some of them are narrow enough that I'll probably never hit that exact trap again regardless of whether it's written down. But the pattern — something takes real time to diagnose, the root cause isn't obvious from the error message, and hitting it again cold would waste the same time — is a good enough filter that the file has stayed genuinely useful rather than turning into noise.

What I'd Still Call Unfinished

In the spirit of not smoothing this into something tidier than it is: the harness has gaps I know about and haven't closed. Wrapper drift is an accepted, unenforced risk — if a source role file in agents/roles/ changes meaningfully, nothing automatically flags that its .claude/agents/ wrapper needs a spot-check. The check-docs-consistency catches drift between planning docs, but it explicitly doesn't cover published content — voice and factual accuracy in things like this article are a separate reviewer's job. And the whole thing assumes a level of discipline about reading the phase docs before writing code that is easy to skip when a session feels rushed; the harness makes skipping it visible after the fact (a stale checklist, a check-docs-consistency failure), not impossible in the moment.

What I can say with more confidence is the shape of the trade this is: less time evaluating whether a given line of generated code is locally correct, and a lot more time up front designing the procedure that makes an amnesiac collaborator produce something coherent across a project this size, over a timeline long enough that no single session can hold the whole thing in its head. That second kind of work turned out to be the real engineering problem. The code was, comparatively, the easy part.

If you're running something similar — long-horizon work through an AI agent across a codebase big enough that no one session can hold the whole thing — I'd genuinely like to hear what your version of a memory file, a session-boundary procedure, or a lessons-learned log looks like. I suspect a lot of us are independently converging on the same shape of answer.

Want to know more?

Interested in "The Agentic Harness: What It Actually Takes to Run a Monorepo Through an AI Agent"? Leave your details and I'll follow up with more information.

← All articles