The numbers
| Measure | Value |
|---|---|
| First commit | 11 August 2026 |
| Calendar time | 9.5 weeks, to 16 October 2026 |
| Commits on main | 1,956 |
| Lines of code, comments and blanks excluded | about 201,000 (180,600 of them C#) |
| App code / test code | 98,200 / 90,500 lines |
| Tests | about 3,800 test methods, plus 132 Gherkin feature files |
| Architecture decision records | 205 |
| Projects | 16 app projects, 19 in-box plugins, 23 test projects |
| CI workflows | 13 |
The seven-year figure is an estimate, not a measurement: 25 to 50 finished lines a day per developer, four developers, about 220 working days a year, leaning to the slow end because a compiler whose four backends must agree exactly, and real-time sound drivers on three operating systems, are slow code to write. Against it, the calendar speedup is roughly 40 times.
The jump came from capacity. On 17 September I moved to a Claude Max plan with five times the usage. What the method did was keep that speed from breaking things: in the same fortnight the rules moved into the repo and the gate started running on every push. From 20 September to 16 October:
- Build ran on 624 finished pushes to main, and 99 of them failed (16%).
- Main went red 52 times. 37 of those were green again within an hour, and the median was 25 minutes.
- Eight releases shipped (0.4.0 to 0.9.0), each from a commit that had passed the whole gate, because the release refuses any other.
Things did break. They were caught at the push that broke them, named, and fixed before anything else, and no red commit was ever released.
Build for the agent first
The single most important rule in the repo: when something can be built more than one way, pick the way an agent can drive with no eyes on the window. It outranks convenience and polish. If the only way to confirm a feature works is a screenshot or a person, the feature is not finished.
Flyback is a visual, real-time instrument. Claude can read a screenshot, and sometimes that is the right check, but a screenshot gives an impression, not an answer a script can assert on, and a sound has no screenshot at all. So every question a person would answer by looking or listening also got a command:
flyback-cli check,infoandprint --checksay whether a patch compiles and what it holds.render --atdraws a still,shotcaptures the editor window headless,measureandproberead numbers off the sound.compareanswers "is this still the same instrument" after a refactor.flyback-viewer --hidden --forplays a patch and listens to it.- Every report has
--json, every exit code means one thing, and stdout carries only the answer.
The second half of the rule matters as much: a hack is a feature request. When Claude reaches for reflection into private state, a throwaway program to read one value, screen coordinates or parsing human-readable output, that is the signal a command is missing. It finishes the job with the hack, then proposes the command. Those proposals become to-do items, and the next session never pays for the same friction. The tooling grew out of the work instead of being designed up front.
The repo is the memory
Every session starts cold. Whatever it needs to know has to be written down where it will be read, so the repository carries the knowledge instead of anyone's head.
| Layer | What it holds | How it is loaded |
|---|---|---|
| AGENTS.md | The entry point: what Flyback is and where everything else lives | Every session |
| docs/agents/rules/ | 12 standing rules: git workflow, honest tests, security, saved data, plugin contract, prose style | Every session, in full |
| .claude/skills/ | 17 skills of task know-how: authoring presets, performance, release keys, site screenshots | When the task matches the skill's one-line description |
| docs/adr/ | 205 records of why each design is the shape it is, and which refactors were declined | Before proposing a change of shape |
| docs/glossary.md | One word for each thing, and the words ruled out | Corrected on the spot when I use the wrong one |
| TODO.md and docs/handoff/ | Work asked for and not started; plans too long for a line | Any session picks an item up cold |
Each rule file has the same shape: the rule, why, and how to apply it. The why is what lets a model apply a rule sensibly to a case it was never written for.
Rules come from friction. When a session swept another session's half-finished compiler change into its own commit, the git rule grew a section on isolating from other sessions' work. When the plugin contract grew by a third in one release with members no plugin named, the plugin contract rule was written, with a test to hold it. I rarely write a rule in advance; I write it the second time something hurts.
Architecture decision records do the same job for design. With 205 of them, a fresh session that spots "duplication" can find that the shape was chosen on purpose and the refactor already declined, instead of undoing a week of decisions.
One trunk, one gate, honest tests
Speed only counts if what lands works. The guardrails are classic continuous delivery, applied strictly because the author never gets tired of following them.
- Commit straight to main. Trunk-based development, small commits, many sessions landing on one trunk within hours of each other. Churn is squashed before it is pushed.
- One gate.
docker build --target gate .restores, compiles and runs every test. CI runs exactly that command, and a release is built from the same image, so there is one definition of "passes". - A red main stops the line. Fixing it comes before the task in hand, even if another session broke it.
- Honest tests. A failing test is fixed by fixing the code. Never a wider tolerance, a skip, an exception list or a special case in the source. This is the rule a model is most tempted to break, so it is spelled out item by item.
- The pipeline is code that holds keys. Every action pinned to a commit SHA, every base image to a digest, least permission, timeouts on every job, releases signed.
Architecture is enforced by tests, not by review. ContractSurfaceTests fails
when the public plugin API grows a member no plugin uses. ComponentDiagramTests and
PipelineDiagramTests fail when the diagrams disagree with the project files and workflows. Other
guards fail if a test project is missing from the solution or a workflow splices an expression into a script.
A model reads its failures far more reliably than it remembers a convention.
Coverage, and what the pipeline caught
The tests run 90.6% of the lines and 79.9% of the branches of the code, measured on 13 October 2026. The Gherkin scenarios on their own reach 70.3% of the lines: that is how much of the program is stated as a requirement, not just tested.
| Part | Lines | Branches |
|---|---|---|
| All tests but the scenarios | 90.6% | 79.9% |
| Flyback.Core, the plugin contract | 99.1% | 92.6% |
| Flyback.Engine, the compiler and backends | 95.9% | 88.9% |
| Flyback.Editor | 92.0% | 79.3% |
| The scenarios alone | 70.3% | 50.6% |
The low figures are platform code. The measurement runs on Linux, so the macOS and Windows sound and key-store drivers barely show (macOS input and output: 3.8%), even though a Windows runner tests them on every push and a macOS runner before every release.
How it is measured. A Coverage workflow runs every Monday on the same Docker image as the
gate. Each test project runs under dotnet test --coverage and writes a Cobertura report;
scripts/coverage.sh merges them, counting a line as covered if any project ran it, and prints the
table into the run summary. The scenarios get a figure of their own, because what they reach says what is
specified. There is no threshold and nothing fails on the number: the gate fails only on what is wrong, and the
weekly figure is there to notice when something stops being covered. A second weekly job lists code in the app
that only tests call, so dead code kept alive by its own tests gets noticed too.
What the pipeline caught. The pipeline is how a session finds out it broke something without me noticing. A few real catches:
- Tests that asserted nothing. Ten guard tests were passing without checking anything. Making them skip with a reason, and making the gate fail when a tool it needs is missing, found three real bugs: a sound-only recording that wrote no sound, a module with empty knob values crashing the compiler, and the Linux key store reporting an error for a key it never held.
- Hangs with no clue. A hung test project sat for half an hour until the job was cancelled. The gate now takes a hang dump after ten minutes and names the tests in flight, and every wait in the tests has a cap with a message.
- A break blamed on the wrong commit. Build used to cancel the older run on main, so a break landed with no verdict and the next commit wore its red. Every push to main now gets its own verdict.
- The GPU disagreeing with itself. The macOS GPU run found seven presets whose picture on a Mac's GPU differs from the processor's.
- Tests that silently never run. A test only Windows or macOS can run fails the gate if no runner would actually run it.
- Dependencies that move. The SDK and every base image are pinned by digest, and restores are locked, so a package version that moved fails the gate instead of building.
Every feature ships with its requirement
Every feature lands with a Gherkin scenario written as a requirement, in the words a musician would use, not the words of the code. There are 132 feature files, from modules to the canvas, the assistant, devices and the preset site. A real one:
Feature: Chance lets some notes through and not others
Scenario: At even chance about half the notes play
Given the gate of a sequencer stepping 16 times a second, through a chance of 0.5
When it plays for 20 seconds
Then about half of its notes play
And every note that plays, plays whole
I review the requirement, which is short and in my language, instead of every line of the implementation. And the requirement outlives the code: when a later session rewrites the module, the scenario says what must still be true.
The same idea runs through the engine. The patch compiler has four backends (an interpreter, IL, GLSL and JavaScript), and tests hold them to the interpreter's answer. When a backend differs, the backend is wrong, not the comparison. That is what lets a model rewrite a backend for speed without me checking the output by ear.
I direct, sessions build
My job changed from writing code to running a small team. I drop an ask into a project chat; a coordinating Claude starts a thread for it, and a separate Claude session does the work there: reads the rules, writes the code and the scenario, runs the suites, commits to main and reads CI's verdict. I come back to a result, a question with a recommendation, or a blocker only I can clear.
On 15 October alone I opened 12 threads, among them a Microsoft Store listing, a macOS sound input driver, a privacy policy page, an investigation of a YouTuber's math-music videos and an experiment with a decision model choosing knob settings. They ran side by side. Locally the same thing happens with git worktrees: each session works in isolation and lands on main when its step is whole.
What I keep for myself:
- Deciding what to build, and saying no. Many asks end in a decision record or a proposal I approve or decline, not in code.
- Taste. How a preset sounds, how a panel feels, what a word should be.
- Real hardware. An ASIO driver crash on an AMD card, a USB MIDI keyboard, a phone. Sessions say plainly what they could not check, and that list is my to-do.
- Turning friction into rules. When a session does something wrong twice, the fix goes in a rule, a skill or a test, not into my memory of what to watch for.
Project memory carries the facts that are not in the repo, such as which machine runs which CI runner, so a fresh coordinator picks up where the last one stopped.
What it made possible
The method did not just make Flyback faster to write. It made a scope possible that one person would never attempt.
| Area | What exists |
|---|---|
| Engine | A patch compiler to a flat register machine, with interpreter, IL, GLSL and JavaScript backends held to one answer; no third-party dependencies |
| Picture and sound | One graph drives both; GPU rendering through OpenGL and WebGL 2; hand-written PNG, JPEG, WAV and AVI writers |
| Platforms | Desktop editor on Windows, macOS and Linux; Android editor; the editor and a viewer in the browser on WebAssembly |
| Sound and MIDI | ASIO, WASAPI, CoreAudio, JACK and Android sound, line in, USB MIDI keyboards and controllers |
| Patching | A visual editor and a text language for the same patch, presets with played MIDI voices |
| Plugins | A versioned plugin contract, 19 in-box plugins, signed plugin packages, and a review that decompiles a submission without running it |
| Assistant | An in-app assistant that edits the open patch |
| Distribution | This website and a preset site, signed releases and self-updates, a Microsoft Store package and an Android app on the way |
| Command line | flyback-cli with 18 commands, from render and measure to pack-plugin |
None of these is a demo. Each has its tests, its decision records, its place in CI, and the security checks where outside input enters, written down in a threat model.
What it costs, and where it breaks
This is not free speed, and pretending otherwise would undercut the point.
- Models are confidently wrong. A session told me the project's history began on 13 October, because its copy of the repo was a shallow clone. It corrected itself within a minute, but only because I asked. Claims need evidence attached, and the rules ask for it.
- Parallel sessions collide. One session once swept another's half-finished compiler change, forty shader snapshots included, into its own commit. The isolation rule exists because of it.
- Hardware is still mine. A macOS microphone driver passed CI and has never heard a real microphone. An ASIO crash in AMD's OpenGL driver is still open. A green build is not a played instrument.
- I am the bottleneck. Nobody reads 1,956 commits line by line. That is exactly why the weight sits on scenarios, guard tests and the gate rather than on my review.
- The writing is real work. About 23,000 lines of Markdown: rules, skills, decision records, a glossary, handoffs. It pays for itself, but it has to be kept true.
- Not everyone accepts it. IzzyOnDroid's inclusion policy rejects apps with LLM-written code, which rules Flyback out there.
If you start tomorrow
- Write an AGENTS.md on day one, even if it is ten lines. Point it at everything else.
- Give every visual or physical thing a command that answers with text and an exit code. If an agent cannot check it, nobody will.
- Treat every hack as a missing feature. Let the agent finish, then have it propose the tool that would have made the hack unnecessary.
- Write a rule the second time something hurts, with the why. Put architectural rules in tests, not in prose.
- Record decisions, including the refactors you decline. They stop fresh sessions undoing your choices.
- One gate, run identically everywhere. Never let a test be weakened to make it pass.
- Ship each feature with a requirement you can read, so you review intent rather than code.
- Run work in parallel and stay the director. Your time goes to deciding, tasting and testing on real hardware.
The speedup did not come from a better model alone. It came from building a place where a model's output is checked, remembered and corrected without me in the loop for every step. That place is the thing worth copying. The repository is public: start with AGENTS.md and docs/agents/rules/.