The last post ended on a claim: a loop does not need to be given the domain details — it can learn them. If the intent is written down, and every scenario carries its own bar and its own test, the machinery in between shouldn't care what kind of software comes out.
That is not a claim you can settle by argument. You settle it by pointing the same skill at problems that have nothing in common and seeing what comes back.
So we did. Over June and July, five projects, four kinds of software, 3,185 commits. A browser CAD editor and ninety-nine games. A native iOS app that reached TestFlight. An Android e-reader OS. Firmware for a device on a desk.
This post is the log of experiments, and it is the honest version — which means the second half is about what broke.
The browser
loopexp was the longest-running of them, 1,156 commits from early June to the end of July, and it is not one app but a shelf of them — five, each pointed at a deliberately awkward target, all of them live and clickable at autoloopexp.web.app.
3DBuilder is a parametric CAD editor in a browser tab: sketches extruded into solids, boolean operations between bodies, fillets, push-pull, STEP import and glTF export, on three.js. The screen shows how far past a demo it went — a tool rail from primitives through construct and transform, measure, annotate and section, a materials and environment inspector with light rotation and sun elevation, and a snap system with separate translate, rotate and scale increments. The rounded box in the viewport is a fillet that worked.
CAD is a nasty target, because the bugs are geometric and they compound. The run's own history shows it: a fillet deriving its dimensions from the world bounding box rather than local bounds; baked bodies that had to stay boolean-operable after a colour bake; stale vertex colours surviving an operation that should have dropped them. Those are not the failures of a demo. They are the failures of something being used hard enough to find them.
What it built to catch them is more telling than the features. Two harnesses stand out: an undo/redo bisimulation check — run a mixed sequence of operations, undo it all, and assert the document is indistinguishable from where it started — and a save/load idempotence harness over the whole document model. Nobody asked for those by name. They are what a scenario that says "undo must be correct" turns into when the bar is written before the work.
HomeDesign draws a plan in 2D and stands it up in 3D beside it, live. Walls, openings, furniture, dimensions and rooms; multiple floors; walk and spin camera modes; metric or imperial; a running tally along the bottom — five walls, two openings, seven items, one room, 24.5 m of wall, 30 m² enclosed. The materials are generated procedurally rather than loaded from image files, which is the kind of constraint that only shows up when someone writes it down first. It also ships a cost estimate, a design check, and a Feng Shui and Vastu panel — features a model does not volunteer.
CardDemo is the one I did not expect. It is AWS's CardDemo — the mainframe COBOL credit-card sample — ported to a Firebase-backed web app, and it keeps the original identifiers rather than inventing friendlier ones. The nightly batch pipeline runs CBTRN02C transaction posting into ACCTFILE, then CBACT04C interest accrual, then CBSTM03A statement generation. Account lookup is labelled COACTVWC; the fraud-hold cases in the work queue carry COCRDUPC. There is a draining DALYTRAN queue with a live posting tail, a portfolio snapshot computed from the same ported records the screens read, and seventeen flagged anomalies waiting for an operator.
Keeping those names is the interesting decision. A port that renames everything is unauditable — nobody can check it against the system it came from. This one can be read side by side with the COBOL.
Ninety-nine games
theboardgames is a separate repository and a completely different shape — web technology again, but running on hardware rather than in a tab. Ninety-nine games for the Board, a 24-inch tabletop touchscreen console, built between the 8th and the 11th of July. Three hundred and thirty-five commits, 154,000 lines, 246 test files. About ninety hours end to end, which is a finished game roughly every fifty-five minutes, sustained for four days.
The catalogue is not a set of variations on a theme. Chess, Go, Shogi and Xiangqi. Backgammon, Hnefatafl, Alquerque, Bagh-Chal, Konane. Sudoku, Nonogram, Slitherlink, Nurikabe, Hashi, Kakuro, Akari, Hitori — eight distinct constraint puzzles, each needing a generator that produces uniquely solvable boards and a solver to prove it. A forty-space property game with houses, mortgages, jail and bankruptcy. Real-time arcade: Tetris, Breakout, Pong, light cycles with simultaneous crash resolution.
Every engine came out seeded and deterministic — a mulberry32 PRNG from an integer seed, no Math.random anywhere — because the scenario said so before a line existed. That is the design from Part 1 doing its job. Determinism was not a thing someone remembered to ask for in review; it was in the bar, so it was in the code, ninety-nine times.
The screens are the argument here, because the obvious objection to "a game every fifty-five minutes" is that they must be shallow. Look at what is on them.
Backgammon tracks a pip count for both sides, an off-the-board count, and a doubling cube — Double → 2, with the stake displayed. The doubling cube is exactly the part a thin implementation leaves out; it is the part that makes the game the game. Chess ends with a real terminal state, not a banner: captured pieces tray, material advantage at +9, checkmate detected and named. Tower Defense has eight distinct tower types with separate costs — arrow, flak, cannon, tesla, frost, venom, sniper, beacon — a gold economy, eleven waves, a campaign, selectable maps, and upgrade-or-sell on a placed tower.
The other half of the answer is on the launcher: 99 games · page 1/5, with a Stats button in the corner. Difficulty, series scoring, statistics, undo, sound and celebration were built once as shared systems, and every game inherits them. That is why the marginal cost of game ninety-nine was an hour rather than a week — most of what a game needs already existed, and the vision for each new one only had to describe the rules that made it different.
iOS
Vivora is a health app over HealthKit, and it is the largest thing on the list: 1,452 Swift files, roughly 246,000 lines, 1,413 commits between the 29th of June and the 10th of July. Twelve days.
Its vision file carried six goals and 848 scenarios — 848 separately written bars, each with its own rubric and its own test. It is the clearest demonstration we have that the mechanism holds under weight. The loop was not steered by someone re-explaining the goal each morning. It was steered by 848 statements written at the start.
Look at what those screens are actually doing. The day is divided into four tracked areas — nutrition, activity, body, sleep and mood — each with its own ring, and the top-level number is how much of today is logged, not a score out of some ideal. Sleep is read from HealthKit down to the stage: deep, core and REM, stacked per night, with a running sleep-debt figure and the honest note that this week's average is drawn from three nights rather than seven.
Then there is the layer above the data. The briefing does not just restate the numbers, it correlates them: on workout days, your sleep averaged 6.1 h vs 7.4 h. The coach reads recovery signals and suggests an easier day. A "health council" synthesises several specialist views into one plan. The assistant will log a meal from a sentence of plain English.
The small things are the giveaway, though. Logged by mistake? Undo last entry sits on the home screen — a control that only exists because someone wrote down that mis-logging is a normal thing people do and the app has to forgive it. So does a nudge that reads yesterday's steps slipped, but today is a clean page, and a completion state called Building rather than a failing grade. Those are not features a model invents to fill a screen. They are what 848 written bars look like when a fair number of them are about how the thing should behave rather than what it should contain.
Native mobile is also where the "just run it" assumption breaks hardest. There is no headless browser. And Vivora is not one target but four — the phone app, a watchOS companion, watch complications, and a home-screen widget, each with its own lifecycle and its own way of being wrong.
It shipped. Not to a demo URL — to TestFlight, iterating to build 110, with the release script fighting provisioning profiles like everyone else's does.
Android
booxsuite is an e-ink "day OS" for a Boox reader: 238 Kotlin files, 194 commits, the 22nd to the 31st of July.
The six tabs along the bottom are the whole product: day, calendar, mail, notes, library, focus. The day view is the one it opens on — the date, what's next, today's events, tasks that have not been placed into the day yet, what is already done, a focus tile and whatever you are currently reading, with a capture strip along the bottom. Mail is a real Gmail inbox. Notes are local, filterable by pinned, meetings and ink, with snapshot and restore. Focus is a deep-work timer that inverts the screen and asks for a long press to end.
Now look at the library screen, because it is the best single artifact in this post. Among the books and PDFs sit these files, still on the device:
reader-test-book·reader-test-letter·reader-test-epub-corrupt·reader-test-epub-drm·reader-test-epub-fixed-layout·reader-test-epub-obfuscated-fonts·reader-test-epub-onefile
That is the test corpus, sitting on the hardware where the tests ran. And read the names as a list of questions: what happens when the archive is damaged? When the book is DRM-protected? When it is fixed-layout rather than reflowable? When its fonts are obfuscated? When the whole thing is one file instead of many? Those are precisely the awkward cases that separate an EPUB reader that works from one that works on the first book you actually own.
Nobody types that list while writing a parser. You get it by writing down, before any code exists, what the reader must survive — and then having something that cannot mark the scenario met until a real file proves it. Part 1 argued the bar has to be written first and backed by a test. This is a screenshot of that argument.
Which brings us to what those files were testing. The first thing worth singling out is a complete EPUB engine written from scratch — zip container reader, XML lexer, XHTML block layout, pagination, table of contents, text-encoding detection. Not a wrapper around a rendering library. The parsing stack.
The second is subtler and more convincing: an e-ink refresh policy. Panel types, ghosting, scroll refresh. E-ink is a display that remembers what it showed you, and handling it well means knowing when a full-panel flash is worth the flicker. That knowledge lives in the vision, not in the model's head, and it produced code that treats the hardware as the awkward thing it is.
The commit log has a distinctive voice, and it is worth quoting because it shows what a scenario written in a person's own words turns into: "scrolling surfaces clear their own ghosts." "A responsive email is one message, not two." "Newsletters were rendering from their plain-text alternative, and now do not." Those are descriptions of behaviour, not of code. Which is what the bar was.
The device
esp32den is the smallest run on the list — 87 commits across eight days in July — and the one that best isolates the claim. ESP-IDF and C++ on an M5Stack Tab5: an ESP32-P4, with Wi-Fi arriving over a separate ESP32-C6 co-processor.
There is no way to fake this one. The code is cross-compiled, flashed to a physical board, and either it boots or it doesn't.
What came out is a launcher — depth-two navigation, edge-swipe home — over three folders: System with three apps, Utilities with three, Games with six. A dashboard on live system, power and IMU data. A Wi-Fi scanner driving the C6. A calendar, a doodle pad, and six games including tetris, snake and connect four.
The one on the screen above is Hum, and it is the nicest thing in the run. It watches the accelerometer for the vibration of a machine somewhere in the house and reports in sentences rather than readings: Quiet — nothing's run since I last checked. Then, once something starts, This feels familiar — a rhythm I know well, with the elapsed time and a strip showing the load so far. No numbers, no chart of raw magnitude. A device that reads the building and tells you about it in a sentence.
But the launcher is not the interesting part. The interesting part is that the loop built itself a way to see.
An agent working on firmware has a problem a web project never has: it cannot look at the screen. So the run grew a shared den_core component that exposes the device over its serial console — framebuffer screenshots pulled off the running board, on-device self-tests, a sensor registry that can be read or simulated, and log capture. Alongside it, den_ui gained synthetic input: queue a pointer press at a coordinate, glide it, or walk it along a polyline. And tools/den.py is the host-side client that drives all of it — screenshot, selftest, sense, sim, logs, tail, cmd.
Read that back as a list of capabilities and it is a description of hands and eyes. The loop could tap a button on a physical touchscreen, take a picture of what happened, and assert on it — which is exactly the gap that makes firmware hard to write with an agent, closed by the agent, because a scenario demanded evidence and evidence was not otherwise obtainable.
The same instinct shows up in how it ships. Updates run over esp_https_ota against a pinned CA, onto A/B partitions, with build-time version stamping and a manifest. Then the part worth pausing on: the rollback is self-test gated. The board boots the new image, runs its own self-tests, and if they fail it reverts to the previous partition and persists the reason it rolled back. A firmware update that verifies itself and can say why it refused is not a thing you get by asking for an OTA feature. It is what you get when the bar says an update must prove it works.
Everything the loop needed for this was already on the machine: the ESP-IDF toolchain, the board on a USB port. That is the Part 1 argument arriving at its conclusion. A hosted runtime would have needed a firmware adapter and a device farm. A skill running in the directory where the toolchain already worked needed neither — and when it still could not see, it built the instrument.
So the claim held
Across all of it, the skill did not change. The vision format did not change. A turn was the same turn: pick a task, dispatch one implementer, demand a real test per scenario, commit, score against the rubric, take the next one.
That is the result, and we think it is the interesting one. The variable was the vision. The constant was the loop.
What broke
Now the honest half. None of what follows is a bug we shipped and fixed. Each is something the design was missing — visible only because we ran it this hard, and every one is an answer to the same question: what can this system not talk itself out of?
A loop that grades its own work always passes. This is the big one. When the thing that scores a scenario is the same session that just built it, having just read the rubric describing what good looks like, it returns full marks. That is not dishonesty. It is unfalsifiability. There is no state of the world in which that judgement comes back negative, which makes it not a judgement at all.
Silence reads as success. When the implementer also writes the tests, 0 passed, 0 failed is a reachable outcome. A suite that ran nothing exits zero and looks exactly like a suite that passed. Absence of bad news got treated as good news, and a scenario can be marked met on the strength of a test that never executed.
Done drifts from what was asked. The vision says what to build; the rubric says what counts as done. Keep them as two separate statements and a run will satisfy the second while quietly diverging from the first — confidently, with a green score, because it was graded against the version of the goal that lives in the rubric rather than the one in your head.
Written intent that governs nothing is worse than having none. A constraint recorded in a document that no step actually reads binds nothing while looking exactly like it binds something. That is worse than an empty file, because someone relies on it. If it does not gate a step, it is decoration.
Every run is day one. A run has no memory of yesterday. It cannot say "I tried that and it did not work," so it tries again, the same way. Across a long project that is not one working week — it is twenty-five first days, each starting from nothing and rediscovering the same dead ends.
It only runs while your laptop is awake. The structural one, and the direct cost of the decision that made everything else possible. A skill running in your session inherits your toolchain — and also your lid, your battery, your network. That is fine for a personal loop. A teammate cannot be something that stops when you close a laptop.
What that adds up to
Read them together and they are one shape. Every failure is a place where the loop could reassure itself: score its own work, read silence as success, grade against a restated goal, point at intent that governs nothing, forget what it already tried.
The fix is not a better prompt or a stricter rubric. It is structural — judgement has to come from something that did not do the work, evidence has to outrank opinion, intent has to gate steps rather than describe them, and the thing has to survive a closed laptop.
That is v2, and it is the next post.
