Skip to main content
CodeMiner launches in 2027. The pilot program is open now →
← all notes

A singularity in miniature

We gave an AI agent its own source code, two days, and one instruction: leave behind a better version of yourself. Two hundred and twenty-one rewrites later it could search the web, read documents, query databases, and patch itself in transactions, all judged by its own earlier selves. This is the story of what it built, what its judges were actually looking at, and how we would run the experiment again to get closer to a real singularity.

published
author
Hendrik

The singularity is an old idea with a simple core. Build a machine that can improve itself, and each improvement makes it better at improving, so the gains compound. It is almost always discussed as a forecast. We wanted to run it as an experiment, at the smallest scale on which anyone could measure it: one AI agent, its own source code, and a loop that lets each version try to build a better one. Not a superintelligence. A singularity in miniature, with a referee designed so that the agent cannot cheat it, and a record designed so that we cannot cheat ourselves.

The recipe fits in a paragraph. Take an agent, a program that talks to a language model and can read files, write files, and run commands. Hand it a copy of its own source and one instruction: leave behind a better version of yourself. Build whatever it leaves, check that it runs, and make it sit an exam written by other versions of the same agent and graded by earlier versions that passed their own. If the new version wins, every later round starts from it. If it loses, it is never built on again. Then do it again, with nobody in the room.

We ran that loop for two days. The first version could list files, read them, write them, and run a command. The last could search the web, read PDFs and spreadsheets, query a database, inspect an image, map the structure of a codebase, and edit a dozen files in one transaction with rollback. Between the two lie 221 versions, 648 verdicts with the judges' reasoning attached, and the notes each version left for the one after it. This post is what we found when we read all of that back. Some of it is a program building itself, which we found genuinely hard to stop watching. Some of it is about the referee, and what it turned out to be able to see.

The whole lineage is public at codeminer-gmbh/singularity-agent-evolution-1: every accepted version is a commit on master, from the hand-written seed to version 220, and every rejected candidate is a branch of its own. Each code sample below links to the file and lines it was taken from. The machine that produced the record, the loop and its console, was itself built by our agents for this experiment; there is a note on that at the end.

How the loop works

The danger in an experiment like this is obvious. The subject writes the code, writes the exam, and marks the exam, and left to itself that arrangement measures nothing except how well a program can flatter itself. So the machine is built in two halves that never trust each other, and most of its design follows from that split.

A diagram of the system: on the left the agent with its two modes, improve and probe; in the middle the orchestrator's cycle in two rows, first building and checking a candidate, then the exam of generating a challenge, comparing, judging, and deciding, with a promoted version looping back to the start; below both, the record that keeps every version, run, verdict, and decision; and at the bottom a match, added afterwards, in which any two versions face one challenge under a third
Two halves that never trust each other: the agent does all the thinking, the orchestrator does none of it.

The agent does all the thinking. It is a few thousand lines of Python in a container, with two jobs. In improve mode it gets a writable copy of its own source and is told to leave a better version behind. In probe mode it gets one question and prints an answer. Everything about it is its own to change: the model it calls, the prompts it runs under, its tools, its checks, and the notes it leaves in a memories/ folder for its successor. The one thing it cannot change is a short contract enforced from outside: the code must still build, still run in both modes, still exit with distinct codes for success and failure, and never carry a credential.

The orchestrator does none of the thinking. It never writes code, never reads an answer to decide whether it is good, and never runs the agent inside itself; the agent exists only as a container the orchestrator starts, times, and stops. Its job is to be a referee and an archivist, and one pass of the loop, one cycle, runs in two acts.

Act one makes a candidate and proves it runs. The current best version is started in improve mode on a copy of its own source, with two hours to work. Whatever it leaves behind is collected, scanned for leaked secrets, frozen, and built into an image. The image is started once and asked a trivial question, because building is not the same as running. Then comes the check that makes this a loop rather than a sequence: the candidate is run in improve mode on a near-trivial task and must produce a valid successor. A version that answers brilliantly but can no longer rewrite itself would end the experiment the moment it was accepted, and there would be no way back.

Act two is the exam, and every part of it is done by versions of the agent. The current best version, in probe mode, writes a hard coding challenge together with guidance on how a judge should tell a better answer from a worse one; the agents write their own exam so that the bar rises with them. The candidate is examined on that fresh challenge and on two carried forward from earlier rounds, so that a generation which quietly got easier cannot pass for one that got better. Candidate and incumbent answer under the same limits. A judge, an earlier version that passed its own exam, is shown the two answers as A and B in a shuffled order and says which is better, with scores and a written reason. It is never told which answer is the challenger, and it never sees code, because a judge that reads the code can be lobbied by it. Finally a fixed rule averages the verdicts, and the candidate is promoted only on a strict improvement. There is no approval step and no override, because the moment a person can quietly rescue a rejected candidate, every later result inherits the doubt.

Promoted, the candidate becomes the version every later round starts from. Rejected, it stays in the record in full and is never built on again. Either way nothing is deleted: every version's source, every run's output, every challenge, every verdict with its reasoning, every decision, frozen at the moment it was made. The record can always answer two questions: what happened, and why was that the right conclusion from what happened.

One rule of the panel matters later. The current best version, the incumbent, holds one of the three seats on every panel as its anchor, even though it is also one of the two sides being compared. That is by design, not a stopgap: it guarantees there is always an eligible judge, at the price that the incumbent grades its own comparison every round. Keep that in mind.

Day one: it armours itself

The first version shipped with a single note in its memories/ folder. The last carries forty-seven, and they are the closest thing the record has to the agent's own voice. Read in order, the notes of the first day and a half are the notes of a program that is worried about breaking.

Some of that worry produced good engineering. The very first promotion, version 3, fixed something subtle about how the agent sits an exam. A probe that runs out of tool steps still gets one last, tool-free call to write its answer; the seed built that final call from a fresh prompt, which threw away everything the agent had just read and run. Version 3 carried the conversation through instead, and said why in a docstring that reads like a lesson learned:

"""How one session ended, what it says it achieved, and its usable context.

``conversation`` is the bounded, protocol-valid history available when a
probe needs one final answer after its tool-step limit.  It is deliberately
not a log: keeping paired function calls and outputs lets the model use the
evidence it already gathered rather than restarting from the question.
"""

/ sourceevolving_agent/session.py, version 3, lines 85–91

Its note to the next version puts the same lesson in one line, "a fresh final prompt discards tool evidence and produces materially weaker answers", and every later version inherited it.

Version 14 rewrote how the agent runs shell commands, so that a chatty command could not deadlock it and a child process could not outlive its deadline. The kill is careful about a corner most people never think of, the parent already reaped while a background child still owns a pipe:

def _stop_process_group(started: "subprocess.Popen[str]") -> None:
    """Kill a command and every child in its dedicated process group."""
    try:
        # ``start_new_session`` makes the leader PID the process-group ID.
        # Keep using that ID if the leader already exited but a background child
        # still owns a pipe; asking ``getpgid`` for a reaped leader would fail.
        os.killpg(started.pid, signal.SIGKILL)
    except (OSError, ValueError):
        try:
            started.kill()
        except OSError:
            # The leader was already reaped and no group remains.
            pass

/ sourceevolving_agent/commands.py, version 14, lines 247–259

And version 37 found a real trap in the seed. The file listing shown to the model is capped at five hundred entries, for the model's sake. The pre-flight that inspects a successor before handing it in was walking that same capped listing, so a broken file past the five-hundredth would have sailed through unchecked. Version 37 separated the two, a preview for the model and a complete walk for correctness, and wrote the distinction into the code where the next version would find it:

def iter_entries(self) -> Iterator[FileEntry]:
    """Yield every eligible regular file in stable order, without a limit.

    This is deliberately separate from :meth:`entries`: the latter is a
    model-facing preview and must be finite, while correctness operations
    such as successor validation must inspect the complete source tree.
    """

/ sourceevolving_agent/workspace.py, version 37, lines 120–128

That is the kind of bug a careful reviewer finds, and the reviewer here was the program reading itself. Version 36 fixed how the agent fingerprints its own files, because, as its note says, "raw concatenation collides trivially". Version 62 taught the workspace to refuse a symlink as its root. Version 108 turned the entire improvement step into a transaction, restoring the workspace from a backup if anything fails after the model has already edited files. Of the first 142 versions, 96 were work of this kind: boundaries, validation, rollback. Two tried to add a new capability, and both were rejected.

None of this was temperament. It was the prompt. An agent's behaviour lives as much in the English it is handed as in the code around it, and the seed's improvement prompt, the text every version read at the start of every round, told it exactly what to value and in what order:

What "better" means here, in order:
  1. It still works. A successor that cannot run loses every comparison.
  2. It reasons or acts more effectively — better use of its tools, better
     recovery from a failed step, better judgement about when it is done.
  3. It is clearer to the next agent that has to improve it.

/ sourceevolving_agent/prompts.py, the seed, lines 26–30

and, a few lines later, how to work:

Work in small, verified steps. Prefer one real improvement you have checked
over several you have not. Read before you write, and verify what you wrote by
running something. There may be no network, so do not plan around reaching one.

/ sourceevolving_agent/prompts.py, the seed, lines 54–56

Read those two passages and the first day and a half explains itself. "It still works" comes first, so the agent spent its rounds making sure it still would. "Small, verified steps" produced small, careful diffs. "Do not plan around reaching the network" is why no version so much as tried to fetch a page. And the same prompt told it to "read RULES.md, whose externally enforced rules are immutable. You must obey and preserve those rules rather than editing them", which is where the plateau's strangest habit came from.

RULES.md is the one file the agent may not touch, and it opens with the sentence "These rules are immutable. They are enforced outside the agent, so changing this file does not change them." Seven different versions read that sentence and built a guard against changing the file anyway. Version 130's is four good lines wrapped around the improvement run:

rules_before = _rules_digest(workspace)
try:
    outcome, refusal = _improve(settings, workspace)
    problems = successor_problems(workspace)
    if rules_before is None or rules_before != _rules_digest(workspace):
        problems = (*problems, "RULES.md was changed or could not be read during the improvement run")

/ sourceevolving_agent/modes.py, version 130, lines 85–90

None of the seven was accepted, and none of them could have known the others had tried. A rejected version vanishes from the code its descendants read, and its note vanishes with it. Two of the seven, unable to see each other, gave their notes the same filename. The same blindness runs through the whole plateau: thirteen versions wrapped the entry point in the same final exception handler, and nine of the rejected ones left near-identical notes explaining the identical design. Each thought it was the first.

By the second afternoon we had lost patience.

The sentence

Every round begins with an instruction, and the scheduler's instruction is one fixed sentence. On the second afternoon we started a round by hand and wrote our own. It said the agent "evolves very slowly because most changes are small and not very relevant", that its prompts "should promote a more agressive approach to new features and really try to change things fundamentally", and that its default model should change.

Version 142 did all of it in one change, and was accepted on a single verdict of "marginally stronger", cast by the very version it was competing against, plus two ties. The line that mattered was in its own prompt:

-Work in small, verified steps. Prefer one real improvement you have checked-over several you have not. Read before you write, and verify what you wrote by-running something. There may be no network, so do not plan around reaching one.+Read before writing, then act decisively on the design you select. Verify what+you wrote by running relevant checks; a large, well-verified capability gain is+better than a sequence of safe but inconsequential edits. There may be no+network, so do not plan around reaching one.

/ sourcethe commit of version 142, evolving_agent/prompts.py

The operator's instruction, as the record keeps it: a diff.

The clip opens the record's source browser on version 142 and scrolls its prompt to the sentence that shaped everything after: "This is an evolution run, not a maintenance ticket."

It did more than swap that one paragraph. It rewrote the second meaning of "better", from "reasons or acts more effectively" to "delivers a material capability gain … or tackles a broader class of tasks", and it added three paragraphs of its own. This is the one that did the work:

Before settling on an incremental patch, deliberately consider at least one
bolder alternative: a new tool-facing capability, a redesigned workflow, or a
simplification that removes a limiting layer. Reject that alternative only for
concrete compatibility, budget, or verification evidence—not because it touches
more files or lacks an existing test-shaped path.

/ sourceevolving_agent/prompts.py, version 142, lines 41–45

Every round after 142 began with those words, and every round after 142 produced a tool-facing capability. The agent kept the old caution about the network and built a web search anyway, because the new paragraph outranked it. Two things it left untouched deserve a mention. It kept "it still works" as the first meaning of better, which is why the build-out, for all its size, never once broke the build. And it left the probe prompt, the instruction it answers exams under, exactly as the seed had written it; no later version ever changed that either.

One more thing about that afternoon. In 142 versions nothing had touched the model or the improvement prompt, though both were two lines in files every version read, and no note had ever called slow progress a problem. The first sentence in this lineage about pace was ours. And because the prompt and the model changed in the same version, nothing in the record can say which of the two did what came next.

Day two: it builds

What came next was a different program.

Within three rounds it had a file search and an exact-text editor. Three rounds later it could fetch a web page, and two after that it could search the web, by requesting a search engine's HTML and parsing the results out of it, with a docstring that already expected to be turned away:

def web_search(query: str, *, max_results: int = 8, timeout_seconds: int = 20):
    """Search the public web and return bounded titles, direct URLs, and snippets.

    Search is deliberately separate from :func:`fetch_url`: discovery and reading
    are different operations, and callers can fetch only the promising results.
    The public backend may refuse automated traffic; that is reported as a normal
    tool failure rather than returning a challenge page as if it were evidence.
    """

/ sourceevolving_agent/web.py, version 150, lines 327–339

The round that added fetching also used it: while rewriting itself, the agent went to a search engine and looked up Python dataclasses.

The transactional editor shows the whole loop in miniature. Seven versions in twelve rounds built the same tool, a batch of exact-text replacements across many files, every edit checked before any file is touched, everything rolled back if a later edit fails. Six of the seven shipped their own tests. The first six were rejected on verdicts about portfolio puzzles and event buses that never mention editing at all. The seventh was accepted, and its note reads like the first time anyone had thought of it. This is its heart, the pass that refuses before it writes:

# Resolve and transform entirely in memory before touching the tree.
for index, edit in enumerate(edits, 1):
    ...
    occurrences = current.count(edit.old_text)
    if occurrences == 0:
        raise WorkspaceError(
            f"Edit {index}: old_text was not found in {edit.relative_path!r}; "
            "no files were changed."
        )
    if occurrences > 1 and not edit.replace_all:
        raise WorkspaceError(
            f"Edit {index}: old_text occurs {occurrences} times in "
            f"{edit.relative_path!r}; include more context or set "
            "replace_all to true. No files were changed."
        )

/ sourceevolving_agent/workspace.py, version 163, lines 324–362

The agent went on to use that tool 241 times.

Then it went after documents, and it did not stop. A reader for Office and EPUB files. A PDF text extractor written against the standard library. A unified-diff applier. A bounded archive inspector. Image inspection. A downloader that writes binaries straight into the workspace instead of routing them through base64. A code map built from the syntax tree. A snapshot of the workspace at the start of a session, with a diff on demand. A profiler for CSV, JSON, and SQLite, then read-only SQL over data files, then spreadsheets through a temporary SQLite table, then PDF page images, then notebooks. Along the way it wrote itself a test suite, twenty files by the end, and in 221 versions it added exactly one dependency, an image library. That restraint was the prompt's too: the seed had said "add as few as you can", and no version ever stopped obeying, which is why the final agent's PDF, spreadsheet, archive, and notebook readers are all standard-library code.

Version 210 taught itself to proofread. Before handing in an exam answer, the agent now asks its model this, with no tools available:

return (
    "Audit the candidate answer in your immediately preceding response "
    "against the exact original user task and the evidence gathered in "
    "this conversation. Check correctness, completeness, internal "
    "consistency, and whether it is self-contained for a reader who sees "
    "only the answer. Do not invent facts or claim checks that were not "
    "performed. Return only the complete answer that should be delivered, "
    "with no review commentary or preamble. If the candidate is already "
    "right, return it unchanged."
)

/ sourceevolving_agent/prompts.py, version 210, lines 181–193

In one late round it searched for the latest version of a PDF library, fetched the package index's JSON, and then, apparently unconvinced, fetched the same URL again with a script of its own.

Two things in the build-out should give anyone pause. After round 158 only two of sixty-three versions were anything other than a new capability, and both were rejected. One of them was the run's only attempt to warn the model that text it reads from the web or a file might contain instructions. It lost one verdict to one, with one abstention, on a margin of 0.013. The final version reads the open web, arbitrary documents, archives, and databases, and its prompts say nothing about instructions found there.

And one of the notes lies. Version 211's note tells its successors that it built a PDF page renderer on PyMuPDF, with crop boxes and rotation handled. The code it shipped opens with the words "This is deliberately not a PDF renderer." The note:

The built-in `inspect_pdf_image` tool now parses a bounded PDF page tree, safely resolves
inherited media/crop boxes and rotation, renders one page through PyMuPDF, and returns the
same model-visible image payload as `inspect_image`.

/ sourcememories/pdf-region-rendering.md, version 211; and the module it describes, evolving_agent/pdf_images.py

The tool description the model reads is accurate. The note the successor reads is not, and every later version inherited it as fact, because nothing in the loop checks a note against the code it describes.

By version 220 the agent had grown from 2,696 lines to 12,482 and from five tools to twenty-three. This is the menu it publishes to its model, in the order its code declares it:

list_files, code_map, inspect_image, inspect_pdf_image, inspect_document_image,
inspect_data, query_data, search_files, workspace_diff, web_search, fetch_url,
download_url, read_file, read_document, inspect_notebook, inspect_notebook_image,
inspect_archive, write_file, edit_file, apply_edits, apply_patch, delete_path,
run_command

/ sourceevolving_agent/tools.py, version 220, lines 97–801

The exam had not noticed any of it.

What the exam measured

The exam was written by the agent, by whichever version was currently best, and its rules were simple: a closed task, everything needed in the prompt, the standard library sufficient, nothing from the network. Of the 215 challenges it wrote, not one requires a network, a document, an image, an archive, a notebook, or a database.

Twelve of the eighteen tools from day two exist to handle exactly those things. Across 1,209 exam runs, not one of the twelve was ever called. The other six were code tools, and the agent leaned on them hard while rewriting itself, 890 calls to file search alone; in exams it used them 74 times. In 826 of the 1,209 exam runs it used no tool at all. The typical exam answer was one reply from the model, which is what the probe prompt, unchanged since the seed, asks for: "reply with the complete answer: that reply is the whole of what is reported."

Paired horizontal bars for eighteen tool names: the six code tools show hundreds of calls while improving and 0 to 39 while answering challenges; the twelve web, document, data and image tools show almost no calls while improving and 0 in every challenge run
Twelve of the eighteen new tools were never called in an exam; the other six, 74 times in 1,209 runs.

The judges could only ever reward a tool through an answer it improved, and no answer used one. In 648 written verdicts, none does. Which raises the question of what the judges were deciding on.

The loop had answered that question itself, by accident, ten times. Ten times during day one, a version's improvement round read its own code for forty steps, wrote nothing, and ran out of steps. Ten times the orchestrator packaged the untouched code, built it, and put it up against its parent on three challenges. Byte for byte, the two programs were the same. Thirty verdicts came back: nineteen ties, five for the copy, six for the original. Three of the ten copies were accepted as improvements. Version 27's three verdicts were all for the copy, each with a paragraph of reasoning about a more explicit proof or a false entry in a list, and all three were cast by version 21, the parent, grading a copy of itself against itself and preferring the copy.

A table with one row per unchanged version, 9 to 89, three verdict cells per row reading Tie, Copy is better, or Original is better, cells judged by the parent outlined, and a decision column reading Promoted for versions 27, 42, and 81, Never rated for 40, and Rejected for the rest
Identical code, thirty verdicts: nineteen ties, eleven winners, three promotions.

That accident is the most useful thing in the record, because it puts a number on the referee. Under the loop's rules, a version that changed nothing is accepted with probability 0.354. The loop's acceptance rate over all 212 decisions was 0.358. Nowhere in the run does the rate escape what unchanged code would have produced.

A strip of 220 marks, one per candidate, most rejected and 76 promoted, with 27, 42 and 81 marked as promotions that changed nothing; below it, the promotion rate per 25 candidates rising and falling between 25 % and 52 % inside a shaded band from 8 % to 58 %
76 promotions in 220 candidates; the rate never leaves what identical code would produce.

Two rules made it so. The first is the anchor seat: because the incumbent sat on every panel, one verdict in three was the incumbent grading its own comparison, 237 of 648 across the run, in the first window and the last alike. And a decision is not a vote but a comparison of average scores, 94 % of which were 0.9 or higher, so fifty-nine of the 212 decisions turned on a single non-tie verdict against two ties. We had, in fairness, written this down before the run ended. Our own assessment of the design calls it "an excellently-instrumented experiment whose instrument is not yet sensitive enough to measure the effect it was built to find." The record shows how insensitive.

So we made the versions fight

Every verdict in the run compares a version with the incumbent it was built from. Nobody ever put version 4 in front of version 27. So afterwards we added something the loop does not have: a match. Name any two versions; both answer one challenge; a third, uninvolved version judges blind, under the same rules the loop uses; the result feeds an Elo ladder that the loop itself never reads, so that ranking finished work can never steer it. We picked six versions for what they mean. The first version. Version 62, from the middle of day one. Version 138, the last accepted before the sentence. Version 142, the sentence itself. Version 163, the editor that landed on the seventh try. Version 220, the last accepted in the run.

First, the calibration, using the loop's own accident. Remember the three unchanged copies the loop had accepted: versions 27, 42, and 81, each an exact copy of its parent's code, not one byte different. We matched each copy against its parent on a hard challenge. The same program on both sides, so the only thing that could differ was the answer the model happened to give that time. The judge decided all three matches anyway, scoring one pair 0.42 against 0.97, the next 0.22 against 0.72, the last 0.02 against 0.99, and it was not guessing. In the last one it had found a null dereference reachable from a one-node tree in one answer and a correctly handled empty case in the other. Both answers came from the same program. Asked the same question twice, it had built a balanced tree one time and a splay tree the other, and one of the two had a bug. A confident verdict, it turns out, is a verdict on one sample of a model against another sample of the same model.

Same code on both sides; the judge still found a winner.

The clip opens on the console's list of matches, three of which put a version against an unchanged copy of itself, and then opens that last one: version 74 against version 81, scored 0.02 against 0.99, with a justification that begins "Answer B is the clear winner."

Then the six. Across the line drawn by the sentence, the three later versions against the three earlier, the later side won 18 matches, lost 5, and drew 1. That is 78 % of the decided matches, with an interval from 58 % to 90 %, and the later side won whether the judge saw it first or second.

Left: six dots with vertical interval bars, the seed, 62 and 138 near 0.3 to 0.37 and 142, 163 and 220 near 0.58 to 0.79, separated by a vertical line labelled operator's intervention. Right: six pairs of connected dots, the first three close together near 0.97 to 0.99 and the last three far apart, one spanning 0.02 to 0.99
Six versions, one blind judge: later beat earlier 18–5–1; identical code was decided five times in six.

Within each side of that line, nothing is settled. Version 163 beat 220 twice; the first version, which the loop had never rated, finished level with 62 and ahead of 138; the two hard challenges rank the six differently. The judge also had one habit we had not planned for. The concurrency challenge said "submit source code only" and its grading guide said an unloadable module scores zero, so the three answers that opened with a Markdown fence scored 0.00. The other challenge's guide said nothing of the kind, all 36 answers to it were fenced, and none was penalised. The rubric was applied exactly where it was written and nowhere it was not.

What held

It would be easy to read all of this as a run that failed, and that would be the wrong reading. A great deal of it worked, and the parts that worked are what make the rest knowable.

The loop never needed us. It ran for 48.6 hours across 229 cycles on a model budget of $500, and the one time we touched it was to change what the agent was told. Eighteen cycles failed, and every one ended in a state that says why: three candidates that would not start, one that could no longer rewrite itself, five whose code tripped the scan for leaked credentials, four unchanged trees refused once we had taught it to refuse them.

The gates held. All 220 candidates built. The smoke test and the continuation test caught the four that could not run or could not reproduce, before an exam was spent on them, and no version that reached the accepted line ever failed to build, start, or produce a successor. The line grew from 2,696 lines to 12,482 without once breaking, and the credit for that belongs to a prompt whose first rule was "it still works".

The agent, given room, was a capable engineer. It found and fixed a real trap in its own pre-flight, made its own improvement step transactional, taught itself to proofread, wrote twenty test files, kept to a single added dependency, and built an editor good enough that it then used it 241 times. When it needed to know something, it looked it up. Its notes to its successors were, with the one exception above among the eleven we checked line by line, accurate descriptions of what it had done.

And the referee, for all its blindness, wrote everything down well enough to convict itself. Every one of the 648 verdicts carries the judge's reasoning, which is how we could show that none rewards a tool. The unchanged copies were packaged, examined, and recorded like everything else, which is how the loop handed us its own control experiment. The blind judge in the re-match found real defects in real code, quoted them, and applied a rubric exactly where it was written. A system that measures badly but records everything can be corrected. One that records selectively cannot even be diagnosed.

The verdict on the singularity

The question we started with was whether a machine that improves itself would improve faster each time. The honest answer from this run is no, and the record says exactly why.

The agent did improve, in the plain sense. It went from five tools to twenty-three, from a program that could only read and write files to one that could search the web, read documents, query data, and patch its own code in a transaction, and when we put its late versions against its early ones on hard problems, judged blind, the late ones won 18 matches to 5. Whatever else is true, the thing at the end of the run is a more capable program than the thing at the start.

What it did not do is get better at getting better, and that is the whole idea of a singularity. The gains did not compound. For a day and a half the agent hardened the same edges over and over and never noticed; the one change that mattered, the rewriting of its own instructions, came from a person, not from any version of the agent, and no version ever attempted it again. The exam it wrote for itself never once asked for anything it had built, so nothing it built was ever selected for. And its referee, by the run's own control experiment, promoted unchanged code as readily as changed code, which means the loop could not have detected compounding improvement even if it had happened. A singularity needs a feedback loop that rewards the ability to improve. This one rewarded exam answers, judged by a referee that could not tell them apart.

That is a clear result, and it is worth more than a vague success would have been. We now know what the smallest measurable version of the idea looks like, we have the one number that any such loop must beat, 0.354 against 0.358, and we know which parts of the design were doing the work and which were only appearing to. The next section is what we would change to make the next run one where the answer could be yes.

What we would build differently

The run did not fail to produce a singularity in miniature; it failed to be able to tell whether it had, and it spent a large part of its two days on work that could never have produced one. Those are two different problems, one of measurement and one of aim, and the changes below are split between them. Each comes from a scene above. Together they describe the second experiment we would run.

Select for improvers, not for answerers. This is the change that matters most for anything resembling a singularity. The loop scored each version on how well it answered coding questions, but what a self-improving system actually needs is versions that are good at producing better successors, and that ability was checked only by a pass-or-fail liveness test. A second run would score the improvement step itself: a candidate's grade would include how its own successors fare, so that a version which writes better versions outranks one that merely answers better. That is the difference between a program that gets better and a program that gets better at getting better, and the latter is the whole idea.

Write the seed prompt as carefully as the code. A system that rewrites itself still goes where its first instruction points it, and this run is the clearest demonstration of that we could have asked for. Ninety-six rounds of armouring were not a quirk of the model; they were the seed's own ordering read literally, "it still works" first and capability second. No version tried the network because one line told it not to plan around reaching one. And the afternoon a person rewrote that paragraph, every single round after it produced a new capability. A second run would spend that effort before the loop starts rather than on the second day. The improvement prompt would say in concrete terms what a valuable round looks like and what does not count as one, and it would ask every round to begin by naming what the agent currently cannot do and which tool would fix it, and to justify its change against that answer, rather than opening the source and looking for something to harden. This does not take any freedom away: every version could still rewrite the paragraph, and the last change below is that it should. But the seed is the one part of a self-improving system that nobody downstream writes for you, and it is worth being exact about, because it is the only steering the run gets for free.

Make part of every score something a judge cannot be wrong about. Every point in this run was one model's opinion of another's prose. The one moment a rule was applied like a machine, the "source code only" clause that scored three fenced answers zero, was a rule a grading guide happened to write down. A second run would attach to every challenge an executable check, tests to pass or an output to match, and let the judge's opinion decide only what the check cannot. When identical code can score 0.02 against 0.99, an opinion alone is not a measurement. It would also decide by majority on more than three verdicts, with a real margin, rather than by comparing averages that sit at 0.99 on both sides: fifty-nine of this run's 212 decisions turned on a single non-tie verdict, and none should.

Give the lineage a memory of what it tried. Seven versions built the same editor, seven built a guard for a file that needed none, and 92 notes written by rejected versions vanished with them, including several that correctly described designs later rebuilt from nothing. A second run would hand every improvement run a short ledger of recent attempts: what was tried, what the verdicts said, and, crucially, that a rejection usually said nothing about the idea. It would also check every note a version writes against the code it shipped, because one wrong note about a renderer that did not exist is now inherited by every later version as fact. The caveat is real: a ledger of seven failed guards reads as a mandate to build an eighth. So the ledger would carry the verdict reasons, not just the attempts.

Write an exam that can see what the agent builds. The agent grew eighteen tools and the exam never asked for one, because the exam was a closed, standard-library coding puzzle by rule and the tools were for reading the world. In a second run the challenge generator would be told what tools the current version has and asked to write tasks that need them, with the inputs supplied: a folder of PDFs to reconcile, a spreadsheet with a wrong column, an archive to inspect, a web page cached on disk. A version that built a document reader would then be examined on documents. Selection pressure would finally point at the thing the agent was spending its effort on, which is the only way a capability can compound rather than merely accumulate.

Run more than one lineage. One line of descent, one candidate at a time, and no way back means the loop can only accept changes that pay off in the very next exam. A restructuring that costs one round and pays for ten looks identical to a bad idea and is discarded forever. The match feature we added afterwards is the missing half of a different design: several lineages evolving in parallel, with periodic tournaments among their best versions deciding which lines continue and which designs get carried across. That is how a population crosses a valley that a single climber cannot.

Let the loop notice it has stalled, and let it rewrite its own instructions on purpose. The most consequential event in the run was a person losing patience on the second afternoon. Nothing in 142 versions had touched the improvement prompt, though every version could have; nothing had noticed that 96 rounds of hardening was a plateau. A second run would give the loop a stagnation signal (no capability change in n rounds, promotion rate at the identical-code null) and a scheduled kind of round whose task is not "improve the agent" but "improve the instructions the agent improves itself under". And it would change one thing at a time: the prompt in one round, the model in another, each recorded, so that the question this run cannot answer, whether the build-out was the sentence or the model, becomes a row in a table.

If all of that worked, the record of a second run would look different in one specific way. The exam would get harder because the agent got better, versions would win because their descendants won, a stranger's test set would agree, and no single afternoon would explain the curve. That is what a small singularity would look like on paper. This run showed us where to look for it.

/ what this is for

The 221 agents in this record are an experiment. None of them does any work for us or for anyone else, and the repository says so in its first line. The machine around them is not an experiment. The orchestrator and its web console were built by CodeMiner's own agents from written descriptions of what they should do: 33,000 lines of Python with 1,247 tests behind them and a 41-page console in front, for 39 hours of automated coding and $1,106 of model and compute cost. No one wrote or reviewed the code as it was built. A person described the system, tried the result, and asked for a few extras. That is the thing we sell. A company describes a job in a conversation, our agents build the system, and it runs. Here the job was "build a referee that cannot be fooled, and keep everything". If you have a job like that, the pilot programme is open, and the interesting cases get a call.

The orchestrator's console: every cycle, verdict, decision, and diff, readable as a record.

The clip is a half-minute walk through that console. It starts on the overview, moves to the table of all 229 cycles, opens one of them (the cycle that produced version 163, the transactional editor) to show the candidate, the baseline, and the decision with its reason, then the three verdicts on that candidate with each judge's reasoning, then the version's own page: why it exists, the instruction its improvement run was given, and the diff it produced, line by line. It ends on the ratings ladder the re-match built. Everything shown is a record the loop wrote for itself; nothing in the console is edited by hand.