---
title: "Content Ops"
url: "https://toddpaulbrownjr.com/builds/content-ops/"
author: "Todd Paul Brown Jr."
description: "Content Ops is Adroit's live, evidence-first content pipeline: discovery, knowledge base and drafts, with a human approving each change. 896 tests passing."
kind: "build"
updated: "2026-09-26T02:50:14+00:00"
---

# Content Ops

## What it is

Content Ops is a console for running a client's knowledge and content. Discovery notes and an open-questions tracker. A knowledge base where documents move through an editorial lifecycle (draft, needs review, approved, deprecated) with version history. A content plan built from keyword data. A drafting pipeline that writes from approved sources. And a one-way sync that publishes approved knowledge into [Sadyr](/builds/sadyr/), so the revenue agent answers from the same documents.

It's live in production, at version 5.12.

## Why it exists

Most AI content starts from a prompt and hopes. Content Ops starts from evidence: what the business actually knows, approved by a person, with sources attached. The AI proposes changes as a changeset, and a human reviews them before anything is written. That's slower than letting the model write straight to the database, and it's the reason I trust what comes out.

We run Adroit on it. It drafted our own website.

## How it's built

Claude Code writes the code, the tests and the documentation; I set the backlog, review, and decide what ships. The app is Next.js and TypeScript with a Python agent layer and PostgreSQL, hosted on Sevalla. Every release has to pass the full test suite and a production build before it's pushed.

It runs one project at a time. At three parallel jobs the process ran out of memory, and the choice was a bigger server or a slower queue. We chose slower.

## What broke

The first time we ran it at real concurrency, rewriting our live website, it produced three production defects in one night. All three were the same habit in agent-written code: writing to a database table by deleting the whole table and re-inserting every row. At one job at a time that's harmless. At three, two jobs collide.

- **Runs:** jobs failed within seconds. Fixed that evening.
- **Artifacts:** the same pattern in a second table. Found through a postmortem-export tool I'd built only hours earlier that night, which summarised 227 failure entries into one screen: 29 duplicate-key errors and 151 stopped projects. Fixed the same night.
- **Polling:** the content-plan screen asked for each of roughly 92 cards separately every 10 seconds, and each request read the whole jobs table. Found in a later log upload: 311 of 323 API errors were pool timeouts. Fixed with one batched request.

The lesson I kept: agent-written code can carry one bad habit across several unrelated tables, so finding it once doesn't mean it's gone. And build the diagnostic tool before the incident that needs it. The postmortem exporter paid for itself before midnight on the night it shipped.

Cost per page isn't on this page yet. Cost capture only started recently and there isn't enough measured history to publish a number I'd stand behind.

## Receipts

| Figure | What | Source |
|---|---|---|
| 839 + 57 | TypeScript + Python tests passing, 0 failing | `state/repo-state.md`, last full run at f861f98, pulled 2026-09-25 |
| 92 of 95 | planned website pages drafted in two days of batch runs | manifest B-03 / B-08, 2026-09-08 to 09-09 |
| 74 + 17 | website pages updated + created, live 2026-09-23 | manifest B-03 |
| 3 | production defects found and fixed the same night | manifest B-08, 2026-09-08 |
| 24 | approved public documents published to Sadyr | manifest B-02, 2026-09-21 |

All measured. More on the [receipts page](/receipts/).
