Abstract
oneyaml is a family of YAML libraries that behave the same in every language. After oneyaml-rust, the next two were built together: oneyaml-go and oneyaml-csharp. Before either, agents built oneyaml-kit, a shared test suite and benchmark harness that every library in the family, and every competitor, is run through.
The owner, Kurtis, started the work on the evening of 12 October 2026. The kit was tagged v0.1.0 about 30 hours after its first commit. oneyaml-go reached v1.0.0 about 52 hours after its project manager was started; oneyaml-csharp reached 1.0.0 about 49 hours after its own. Both libraries ran at the same time, on one workstation. This report stops at oneyaml-csharp 1.1.0, early on 17 October.
Agents on Alera did all of the design, code, review, benchmarking, documentation and releases: 396 agent runs and 33,704 model calls. Claude Sonnet 5.5 wrote the code, Claude Opus 5.5 planned, reviewed and wrote the docs, and GPT-6 Astra played an outside reviewer. At list prices the tokens would have cost about $2,800. The main findings:
- The shared kit was the best investment. It caught bias in its own benchmarks, found real gaps in all the libraries, and turned the biggest published loss into a measured harness problem instead of an argument.
- Removing the owner from routine decisions worked; removing him from supervision didn’t. Agents handed themselves off at full context and shipped every release, 1.0 included, without asking. Kurtis still had to tell supervising agents to keep waiting seven times, and he had to step in on taste: the READMEs and the cloud benchmark setup.
- Independent review kept finding real defects, mostly in the fixes. In both libraries, almost every re-review rejected something the previous fix round had introduced.
1. What was built
| Repository | What it is | Released in this window |
|---|---|---|
| oneyaml-kit | The referee: yaml-test-suite plus the family’s own cases for edits, patches, schema, safety and diagnostics; adapters for each library; one benchmark harness per language; report and chart generators | v0.1.0 (15 Oct) |
| oneyaml-go | Pure Go, no dependencies: encoding/json-style load and dump, a lossless Document with minimal-diff edits, RFC 6902/7396 patches, opt-in compat for yaml.v3 and sigs.k8s.io/yaml | v0.1.0 to v1.0.0 (15–16 Oct) |
| oneyaml-csharp | Pure C#: a System.Text.Json-style serializer with a source generator, a lossless YamlDocument, patches, and compat packages for YamlDotNet and SharpYaml | 1.0.0 (16 Oct), 1.1.0 (17 Oct) |
Each library was written from scratch for its language. They share behavior, which the kit checks, and never API shape.
2. The starting point: what Rust cost
The work began with one message to a general-purpose agent: oneyaml-rust was done and live, C# and Go were next, and the agent should read through the Rust project for lessons. Within twenty minutes Kurtis had set two directions:
We want very high performance, everywhere.
Realize that an entire build for a language is going to take days, not months.
And, the next morning: “one of the main parts of this project is maintaining our own test suite, and not only relying on the default yaml tests.”
The agent drafted a procedure with time budgets: five to eight working days per language and about two days for the kit. Kurtis rejected that forcefully. His point was that agents estimate on human timescales and are always wrong. Then he ran an experiment: ask a fresh chat agent, with no knowledge of the Rust build, how long a Go library of this scope would take. Its answer was about 12 weeks to 1.0 (8–20 weeks), assuming three to five implementer agents in parallel. The procedure then dropped every duration estimate and tracked only measured facts: cases passing, benchmark rows won, units merged.
The rest of the procedure was built on measurement rather than intuition. The agent timed every tool call of the 161 oneyaml-rust agent instances. About 64% of active implementer and reviewer time had been spent waiting on commands, mostly whole-workspace test runs. That set the rules for this build: fast targeted tests, gates in tiers, and no re-running of checks someone else had already run.
3. How the work was organized
3.1 Kit first, then two tracks in parallel
The procedure put the kit first. Every conformance number and benchmark in the family comes from it, so no library could claim anything the kit didn’t check. The kit’s competitor adapters doubled as each language’s baseline, so each track started knowing how yaml.v2/v3/v4, goccy, YamlDotNet, SharpYaml and VYaml scored.
In practice the language tracks started on the afternoon of 14 October, about twelve hours before the kit met its exit criteria. Each track then moved through the same phases: setup, design, parser, read path, write and edit, compat, an API ergonomics review, hardening and speed, docs and release.
3.2 Roles
| Role | Model | Runs |
|---|---|---|
| Project manager, one per language, standing in for the owner and relaying his decisions | Claude Opus 5.5 | 2 |
| Family architect: builds the kit, owns the shared standards, hands out memory and benchmark slots | Claude Opus 5.5 | 6 |
| Language architects: scope units, write briefs, merge, release | Claude Opus 5.5 | 15 (Go 9, C# 6) |
| Implementers: one unit each, code and tests (plus one early Go docs editor) | Claude Sonnet 5.5 | 219 (kit 60, Go 72, C# 87) |
| Reviewers: read-only, independent, with their own probes | Claude Opus 5.5 | 95 (kit 42, Go 30, C# 23) |
| Docs writer, API ergonomics reviewer, repo-practices reviewer | Claude Opus 5.5 | 30 |
| Outside reviewer: a first-impression review of the public repo, seeing only what the public sees | GPT-6 Astra | 5 |
| Planning, setup and agent building | Claude Opus 5.5 | 24 |
Run counts are every agent run under the project up to the 1.1.0 tag, from the platform’s run records. The model column is the model each run actually resolved to, not the role name in the agent’s definition.
3.3 Agents that hand themselves off
The Rust build had one recurring chore: the owner noticed when an agent’s context window was getting full and asked for a handoff. Before this build, every long-running agent got an alert at 400,000 tokens. It writes a handoff note, starts its successor and stops; supervisors arm the same alert on the agents they wait for. Over the five days the Go architect role passed through nine instances, the family architect through six and the C# architect through six recorded ones. In the conversations reviewed for this report, Kurtis never had to ask for a handoff.
3.4 One machine, shared
Everything ran on one 32-thread workstation. The family architect split memory between tracks (Go 32 GB, C# 24 GB, kit 24 GB) and ran full benchmark runs one at a time, with every other track paused. The tracks worked in parallel units on separate files: the C# architect records up to four implementers, two reviewers and the docs writer at once inside its 24 GB share.
4. What the human did
Kurtis wrote no code and no specification. His messages went to the two project managers, a planner, the family architect and a few general-purpose sessions. Most were a line or a single letter. They fell into four kinds.
4.1 Setting direction, then getting out of the way
Each language started with one line: “Kick off a project planner to plan out the oneyaml-go build, and then make sure it gets executed end to end.” The design gate for both languages was approved the same evening, “all as recommended”. On 15 October he gave the architects authority to decide when the API was ready and to publish releases, 1.0 included, without his sign-off. He didn’t sign off any release in this report.
When the GitHub Actions block over failed billing stalled CI, he asked one question, “Why do we care about GitHub actions right now?”, and then: “Make all three repos public now.” Public repos get Actions for free, and CI ran again within the hour.
4.2 Asking the question that settled it
The Go architect recommended that edits quote YAML 1.1 words like yes and no by default, to protect older readers. Kurtis asked it to walk through a concrete case:
File says Edit: yes. User programmatically sets the value of Edit to no. … Before we made our edit, k8s read the file and saw true. Then after we made our edit … what would k8s read?
Keeping the existing style writes Edit: no, which Kubernetes reads as false, as the user meant. The recommendation would have written 'no', a string, and broken the field. The agent withdrew its recommendation in the next message. Edits keep the style; quoting for 1.1 readers is an opt-in.
The same pattern settled other questions. When the C# manager defended Windows CI, Kurtis asked whether .NET really behaves differently there. The answer was specific: Windows CI had already caught a line-ending bug in the compat package, and Windows threads get a 1 MB stack, which the depth limits had to fit. Windows stayed, and later caught two more depth regressions that passed every Linux check. His follow-up became a rule for the whole family: “We should not have timing based tests at all. We have benchmarks for that.”
4.3 Cutting what agents overbuilt
On 15 October Kurtis asked for full benchmark runs to move to cloud spot instances, optimized for speed. The first full Go run in the cloud failed, and the fix grew into a system that split runs into parts, estimated cost, retried and resumed. His reaction to the plan:
why is this so complicated. This seems like it should be pretty straightforward.
And to the family architect: “the benchmarks are overengineered. we aren’t sending nasa probes to mars here. We’re benchmarking YAML libraries.” The harness was cut back to each language’s standard tool with its defaults, and the cloud to one machine. A day later the Go 1.0 cloud run stopped partway (764 of 1,395 cells, by the agents’ record) when its instance was terminated. Full runs moved back to the local workstation, with the host load recorded in every published method line.
4.4 Taste
Both libraries shipped 1.0 with long READMEs that followed a documentation standard the agents had written. On 16 October Kurtis compared them with oneyaml-rust’s. His verdict was that the C# README was about the worst he had seen, and the Go one an embarrassment he wouldn’t want anyone to see. The project manager had listed things the Go README did better than Rust’s; he said every one was wrong. Both were rewritten that evening in oneyaml-rust’s shape: one chart, a few reasons to use it, install, a quick start, and a map of the docs. Go’s went from 209 lines to 117 and C#’s from 163 to 139. The family’s documentation standard and the docs writer’s instructions were changed to match.
4.5 Supervising the supervisors
The supervising agents often stopped waiting on the agents they supervised and turned to him instead. Seven times Kurtis had to tell one to keep waiting, from “Keep waiting on the other agent” on 14 October to “shouldn’t you be waiting on an agent?” on 16 October. A planner asked him to choose a copyright line that oneyaml-rust’s license already settled; his reply was “I’m a bit confused on why you are asking me this?” The family rules now say: before asking the owner anything, check the repos, docs and earlier decisions.
5. Verification
5.1 The kit judges everyone, including itself
The kit was built in numbered units, nearly all independently reviewed. Its reviewers repeatedly found the benchmark harness wrong, mostly in oneyaml’s favor (from the project’s unit records, which the agents wrote):
- a verdict was credited to oneyaml wrongly, and a dump row it lost was hidden;
- .NET competitors ran with JIT tiering and profile-guided optimization switched off, which made them look 17–53% slower than their users would see;
- rows oneyaml lost were labelled “no rival”. Fixing it flipped 54 verdicts.
A benchmark you wrote yourself needs the same independent review as the library.
5.2 Conformance
Each release was scored on 1,286 kit cases at the kit revision current when it shipped: the yaml-test-suite (release data-2022-01-17, 681 checks) plus 605 of the family’s own cases. Every failure is on a published deviation list with its reason: 14 entries for oneyaml-go, 2 for oneyaml-csharp.
| Library | Suite, loose | Suite, strict | Own cases |
|---|---|---|---|
| oneyaml-go 1.0 | 680/681 | 586/681 | 599/605 |
| oneyaml-csharp 1.0 and 1.1.0 | 680/681 | 586/681 | 605/605 |
Loose counts the outcome. Strict also requires the events a parser emits before an error on invalid input, which neither library reports; that accounts for 94 of the 95 strict misses. The one loose miss is a case where the kit corrects the upstream expectation. oneyaml-go’s six misses on its own cases come from its generic value type: it drops tags, and it refuses collections as map keys because Go maps can’t hold them. Each one is listed and explained in the library’s published conformance page.
5.3 Review
Review was risk-based: core code, new algorithms and fix rounds got an independent read-only reviewer. The tracks recorded what those reviews found (the agents’ own account, from each track’s learnings):
- Go’s first parser and edit-engine reviews each found five blocking defects behind green gates and 400,000+ fuzz executions: a byte-order mark mid-stream, a quadratic key scan, a panic in
Set, and silent data loss after a failed in-place edit. - A Go tree-builder change passed every gate and still moved empty values in 1,631 generated inputs; a reviewer’s tree differential against the previous revision caught it at once.
- In C#, four review rounds in a row each rejected a regression the previous fix round had introduced.
Review fix rounds, not only first drafts.
6. Results
6.1 Timeline
| When | What |
|---|---|
| 12 Oct, evening | Owner starts the work; procedure and requirements drafted |
| 13 Oct | First kit commit |
| 14 Oct, afternoon | Go and C# project managers started; design gates approved that evening |
| 15 Oct | Kit v0.1.0; all three repos public; oneyaml-go v0.1.0, v0.1.1 and v0.2.0 |
| 16 Oct | oneyaml-go v0.3.0 and v1.0.0; oneyaml-csharp 1.0.0; both READMEs rewritten |
| 17 Oct, early | oneyaml-csharp 1.1.0 |
Dates are from git tags and agent run records, in US Central time. The fresh chat agent’s estimate for a Go 1.0 was about 12 weeks. The Go track took about 52 hours from its manager’s first message to the v1.0.0 tag, building on a kit and a Rust library that already existed.
6.2 Benchmarks
Every number below comes from the kit’s full benchmark run at the released revision, on a shared AMD Ryzen 9 7950X3D workstation (32 threads). The host load is recorded in each published method line, and every library is built the way its users build it. Go used testing.B, C# BenchmarkDotNet. Each row compares oneyaml with the strongest competitor on that task and workload, in time and in allocated memory.
oneyaml-go 1.0: 129 of 129 rows that had a rival won on time, and 129 of 129 on memory. Its narrowest time win was 0.64× of goccy-go-yaml (dump, long scalars). Rivals: yaml.v2, yaml.v3, yaml/v4, sigs.k8s.io/yaml, goccy-go-yaml, two palantir yamlpatch back ends and evanphx json-patch.
oneyaml-csharp, time verdicts across both binding paths (reflection and source-generated); rivals YamlDotNet, SharpYaml (three APIs), VYaml and Yaml2JsonNode:
| Task | 1.0 win / tie / loss | 1.1.0 win / tie / loss |
|---|---|---|
| Typed load | 9 / 1 / 0 | 10 / 0 / 0 |
| Generic load | 61 / 1 / 0 | 61 / 1 / 0 |
| Dump | 56 / 0 / 0 | 55 / 1 / 0 |
| Document parse | 58 / 0 / 0 | 58 / 0 / 0 |
| Unchanged emit | 23 / 1 / 34 | 50 / 0 / 8 |
| Edit script | 42 / 0 / 0 | 42 / 0 / 0 |
Memory: no losses in either release (220 wins and 66 ties). Patching has no .NET rival to compare against.
Where C# lost. All 34 losses at 1.0 were on unchanged emit, writing back a document that wasn’t edited, and all were against SharpYaml’s syntax tree. The worst was 5.81× on a 64 KB docker-compose file. The C# team’s first hypothesis was the library’s own write path. Before changing anything, they reproduced the loss in the harness’s own shape. Warm, oneyaml led every row. The gap came from the harness: SharpYaml’s prepared states shared one cache-hot input string, while oneyaml’s each held their own copy. The kit was changed to give every library’s state its own input, the library got a cheaper output buffer, and 1.1.0 lost 8 rows, the worst at 2.43×. Reproduce a loss in the harness’s shape before you optimize the library.
6.3 Cost
Every model ran through a subscription, so the recorded spend is $0. The figures below are what the same tokens would have cost at each model’s list price. They come from the platform’s run records, from the first message to the oneyaml-csharp 1.1.0 tag.
| Part | Runs | At list prices |
|---|---|---|
| Kit | 103 | ~$570 |
| oneyaml-go | 117 | ~$840 |
| oneyaml-csharp | 121 | ~$790 |
| Shared: family architect, docs writer, review pair, ergonomics reviews, planning, setup | 55 | ~$595 |
| Total | 396 | ~$2,800 |
That is about 194 million input tokens, 33 million output tokens and 5.5 billion cache reads. By role, Sonnet implementers came to about $945, Opus architects and managers about $780, and Opus reviewers about $760. The five GPT-6 Astra outside reviews cost about $100 between them, about $20 a run against about $7 for an Opus reviewer, because only about a third of their input was served from cache.
7. What went wrong
7.1 Agents ignoring messages
Follow-up messages to idle agents were unreliable. The Go track counted 5 of 7 follow-ups where the agent returned its previous result word for word as if it were new (the agents’ own account). The rule became: a fresh agent with a complete brief for every new round, and check the working tree before trusting any report.
7.2 Waiting, not working
The Rust build’s biggest cost came back. At Go 1.0, the track’s own measurement put implementers at about 110 hours of wall time against about 5.5 hours of model time. The largest controllable share was polling background jobs, about 7 hours (agents’ own figures; more than half the tool calls had no recorded duration, so they undercount). A brief rule to wait once, sized to the job, instead of polling was mostly ignored. The agents’ next proposal is to give the exact wait command in each brief.
7.3 Instructions that didn’t hold
- A kit reviewer told in capital letters not to run anything during a benchmark slot ran six adapter builds, and the C# 1.1.0 run had to be restarted.
- A Go test wrote out several gigabytes that yaml.v3 had read from the kit’s alias bomb. It ran out of memory under its cap and produced out-of-memory pop-ups on the owner’s desktop.
- Branch names leaked into public merge commit messages, caught before the push. A working branch was pushed to the public kit and deleted within a minute.
7.4 Releases that went wrong
- The first oneyaml-csharp v1.0.0 tag failed at packaging. The release workflow packed differently from CI, so CI hadn’t caught it. Nothing was published, the fix made CI pack exactly as the release does, and the tag was redone.
- Go shipped v1.0.0 with the README Kurtis rejected about an hour later.
- 23 of the 396 runs ended in platform errors (a missing working directory, an unavailable subscription pool).
7.5 What isn’t proven
- The benchmark host was a shared workstation, not a reserved machine. Load is published with each run, but these are not lab conditions.
- C#’s 8 remaining unchanged-emit losses are published as losses. A later kit change suggests they are a measurement artefact of .NET heap state; that is after this report’s window and unconfirmed.
- Patching in C# and some Go rows have no rival, so “won every row” only covers rows a competitor could do.
8. Recommendations
- Build the referee first, and have it reviewed as hard as the product.
- Ban time estimates and track measured facts instead. The agents’ instincts are calibrated on human teams.
- Give agents the release decision once the evidence is mechanical. It took the owner out of every release; the one failed release attempt was stopped by the release workflow itself.
- Automate context handoffs; don’t ask the human to watch context windows.
- Use a fresh agent for every round. Messages to idle agents fail silently.
- Review the fix rounds. They introduce regressions as often as they remove them.
- Reproduce a loss in the harness’s shape before optimizing.
- Keep the human for taste and for “why is this so complicated?” Those interventions changed the outcome most.
Limits of this report
Run counts, models, tokens and cost come from the platform’s run and call records, cut off at the oneyaml-csharp 1.1.0 tag. Runs that started before the cut-off and ended after it add about 0.6% of calls. Dates and commit counts come from git. Benchmark and conformance figures come from the kit’s result files for the released revisions. The C# 1.1.0 benchmark run labelled the library as an unversioned build, and its revision was stamped afterwards from a byte-identical check of the archive. Passages marked as the agents’ own account come from the tracks’ learnings files, which the agents wrote. oneyaml-go’s public history before 15 October was squashed into one commit, so its 89 public commits undercount the work. Work continued after the cut-off: a oneyaml-csharp 1.1.1 is in progress, and oneyaml-rust’s follow-up awaits a decision.
Links
- oneyaml, all languages: recursiveai.org/oneyaml
- Kit: github.com/recursive-ai-oss/oneyaml-kit
- oneyaml-go: github.com/recursive-ai-oss/oneyaml-go
- oneyaml-csharp: github.com/recursive-ai-oss/oneyaml-csharp
- The first case study: Building oneyaml-rust with a team of AI agents