Will ChenWill Chen
← Writingsystem design

Project Cyborg

I had a large block of compute about to expire and spent it on myself. Most of it went into finding out why the thing I built was not an agent.

Will ChenWill Chen7 min

Motivation

I had a large block of compute that was going to expire, so I spent it on myself. Not on a product, not on customers, on the question of what a person can actually get out of a lot of model calls pointed at their own life. The repository I built to do it is called Project Cyborg, and the interesting part is not that it burned compute. Anything can burn compute. The interesting part is what had to be true for the burning to produce anything.

Design premise

The first version of this experiment had a naive theory, which is that more calls make more value. That theory is easy to hold because it is almost true of everything else you can buy. More servers really do serve more requests. More storage really does hold more files.

Model calls do not behave that way, and the reason is that their output is text, and text is not automatically worth anything. If you ask a model a vague question ten thousand times you get ten thousand vague answers, and now you have a reading problem on top of the original problem. The volume works against you, because the one thing you cannot buy more of is your own attention.

What I wrote down at the time, and then kept at the top of the repository as a constraint on everything built afterwards, is that compute helps when it reduces ambiguity, not when it creates more text. That sentence is testable, which is what makes it useful. If a run leaves me with more words and the same number of open questions, the compute was wasted no matter how good the words are. If a run leaves me with fewer open questions, it paid.

The working method that follows is to run hot first and select afterwards: explore in many directions, generate a great deal of material, then compress and promote whatever repeats. The generating half is cheap and the selecting half is the whole game.

The ledger

Before any of the research programs existed, the thing I built was the ledger, because an experiment that cannot say what it spent is not an experiment.

Every model call goes through one wrapper. The wrapper appends a line to an append-only file, one line per call. This is the whole record of a run, in the second engine:

interface LedgerEntry {
  ts: string;              // ISO timestamp at completion
  runId: string;
  program: string;
  model: string;
  inputTokens: number;
  cachedInputTokens: number;
  outputTokens: number;
  costUsd: number;
  ms: number;
  ok: boolean;
  error?: string;
}

Nothing else in the system is allowed to be the source of truth about what happened. A separate database exists for querying, and it is rebuilt from the file, so if the two ever disagree the file wins and the database is thrown away.

The first version had a property I still like: the events did not carry a cost. Cost was computed at read time from a pricing table, which meant that when prices changed I could correct every historical number by editing one table, without touching a single recorded event. Facts about what happened stay separate from opinions about what they were worth. The second engine moved cost to write time, and that is a real tradeoff rather than an improvement, because writing the cost makes the ledger self-contained and makes the price table historical.

When I rebuilt the engine in June, I rebuilt the accounting from scratch rather than porting it, and the reason is written into the new engine's README: the old machinery had never been verified. An unverified accounting layer is worse than none, because it produces numbers you half-believe.

The research programs

The research itself runs as a set of programs, each one a prompt shape with a model assigned to it. Seven of them do the searching, and three more turned up later: two that close the loop, described further down, and one that ran a single time.

  • insight-miner on the cheapest model, looking for connections across documents that I never stated in one place
  • product-forge on the strongest model, which must return a mechanism, the smallest real test, and a kill criterion
  • tech-mechanisms in the middle, looking for primitives that dissolve frictions that keep recurring
  • reality-contact on the strongest model, turning thinking loops into something that produces a signal from the world, with one design drafted all the way
  • ensemble on the cheapest model, answering a fixed bank of questions many times independently
  • wildcard in the middle, designing its own experiment on the corpus and reporting hypothesis and result
  • synthesis on the strongest model, running every forty runs or so over everything produced since the last one

The corpus is a local snapshot of about nine thousand documents. The engine can read it and cannot write to it, and every write the system makes goes somewhere else.

The ensemble program is the one that best shows the philosophy. It does not ask a question once and treat the answer as an answer. It asks the same question many times, independently, and then counts. An idea that appears in one run is a model producing plausible text. An idea that appears in most of them is a property of the material. Repetition across independent samples is the cheapest signal detector available, and it costs nothing but calls, which is the resource I had.

The open loop

The synthesis program was already doing the right thing. Its prompt asks for exactly three new questions that the outputs raise and that the next wave should chase, and it maintains a short list of the sharpest currently open ones, dropping the answered and the stale.

Those questions went into a markdown file, and nothing read them.

That is what open-loop means, and it is worth being precise about why it matters, because the system did not look broken. It ran, it produced artifacts, it produced good questions about its own artifacts, and every wave started from the same place as the last one: a random sample of the corpus. The sampler had no memory of what the synthesizer had learned. Ten thousand calls arranged that way are not an agent doing research, they are a very expensive random number generator with excellent prose.

The machine-readable channel

The fix took one day and it is small. A harvester scrapes the FRONTIER questions out of the synthesis files into a line-delimited file. An entry is three fields:

{"ts":"2026-06-11T19:13:28.725Z",
 "q":"What is the minimal process primitive that unifies organs, documents, triggers, and agent runs?",
 "source":"2026-06-11181420-synthesis-00193.md"}

The source field is what makes it a loop rather than a queue, because every question can be walked back to the artifact that raised it. A new program, frontier-chase, takes one of those questions as its input and researches it directly, on the strongest model. A second new program, deepen, takes a prior artifact and second-passes it against a fresh slice of the corpus, auditing it, strengthening it, producing a revised version and then a verdict of promote, revise, or kill.

The loop that results is explore, synthesize, generate new questions, explore those. It seeded in one burst with 134 questions pulled out of 26 synthesis files. It currently holds 413.

The mechanism worth stealing here is not the specific programs. It is that a feedback loop needs a machine-readable channel between its halves, and prose is not one. The synthesis output was always full of good questions, but they were embedded in paragraphs, which is a format only a human can act on. Adding one harvester that pulls them into a file with a stable shape is what converted a producer of essays into something that steers.

The harvesting is imperfect in the way this kind of thing always is. Five of the 413 entries are not questions, three of them visibly a bullet caught mid-line, because the extractor is reading structured meaning out of formatted text. That is a 1.2 percent error rate on a channel that costs almost nothing to run, which is a trade I would take every time.

What the engine produced

The engine recorded 6,211 completed runs, one ledger line each. By program:

ProgramRuns
ensemble1,278
insight-miner1,070
tech-mechanisms942
product-forge748
deepen648
wildcard511
reality-contact484
frontier-chase380
synthesis149
canon1

The two programs added to close the loop account for 1,028 of those runs, so the loop did not just exist, it ran.

What I will not claim is that the experiment reached a conclusion, because the repository does not contain one. The nearest thing to a stated result is the title of the first field note I wrote out of it, which is that extraction beats generation, and that agrees with the constraint I started from rather than testing it.

The thing I would defend is the shape. An experiment that spends a lot of compute needs an accounting layer you trust before it needs research programs, needs independent repetition rather than single answers if it wants signal, and needs a machine-readable path from what it learned back to what it does next. The first engine had two of those three, ran well, and could not accumulate. The gap between a system that produces and a system that improves is one file and a consumer for it.