Devlog

Describe your game's rules and Ludira (ludira.io) turns them into a playable, rules-enforced prototype — no code, no manually re-explaining rules to every playtester. Short, dated notes about what actually got built. Entries describe work that is done and verified at the time of writing — this is a log, not a roadmap.

The winner control works. Four separate failures kept it from being used.

Ten scripted playtesters went at the live product over the 8th to the 10th — simulated agents following written briefs, playing people who have never seen the editor, some of them on a phone and nothing else. Round 7's headline problem is fixed for the shape it was built for: three of them authored a per-player score, a move that adds a piece's value, and a "most wins" ending entirely by clicking, and two of those three watched it name the right player — 22 to 13, and 23 to 5. Four separate failures then kept the rest from getting there. Two testers never found the winner control. One found it, used it correctly, and was told the finished game was a draw. One finished a game at 34 to 27 and was shown "lastMark wins." — the engine's own unresolved token, printed where a name goes. One followed the product's own "Add an ending" button and landed in a game whose ending condition was already true at setup, so it was over before the first turn.

The second finding is new, and it is not a missing feature. One tester built a game, signed in, and pressed the product's own "Claim this draft". The server took it and kept it — it survived a sign-out, a wiped browser and a second sign-in. No screen then showed it back to her: no list, no link, no id, on either of her two machines, while the empty editor told her "nothing is saved to an account, it stays in this browser." Her own summary was that the storage works and the screen is missing. Claiming the draft was also what removed her last local route back to it.

The pattern under both is one thing. This round's worst moments were the product making a confident claim the reader could check and falsify. "It's a draw" over 11 to 6. "The tab likely closed or crashed" after a click away and back. "Nothing is saved to an account" to someone whose game was in their account. None of these is a missing message; they are wrong ones, and each cost more trust than the defect underneath it cost work. One tester, verbatim: "A tool whose diagnostics are wrong is worse for diagnosis than a tool with none, because I stop checking."

What shipped since, in two pushes. A recorded winner that does not resolve to a seat at the table is no longer printed as though it were a player — not on the ending banner, and not in the state readout beside it, which was still showing "winner lastMark" after the banner had stopped. The front door lists the drafts in your account, which is the screen that was missing. The save indicator distinguishes between work held in this browser and work held in an account, rather than naming only the browser.

The play screen speaks less engine. The expanded cards printed the engine's own variable names — a GAME card reading "pile / lastTaker / winner / nextMark", player cards reading "mark / score" — a couple of inches under chips that said the same things in plain words. All ten testers reported some form of this, and three confirmed they had never touched the "Show the internals" toggle meant to govern it; it turned out to reach one surface out of four. It now also covers those cards, the piece faces and the hand dock, and switching it on still returns the engine names on those surfaces. On the authoring side, the Rules panel now puts a plain-English reading above each condition and query — "every power plants where its location is deck" above the grammar that produces it. The grammar stays underneath, unchanged. An expression containing anything the reader does not recognise shows the original by itself, rather than a partial sentence that could drop a clause silently.

Several authoring fixes landed alongside those. A move can be deleted and renamed from the editor, which previously offered no way to remove or rename one. Two moves that would show players the same button label are now refused at the point you name them — and a game that already has such a pair says so, names the other move, and offers a one-click relabel. The warning on a number past its declared maximum used to say the engine would clamp the value, which it does not do — a maximum here is a declaration, not a rule enforced while the game runs — so the editor now says that rather than promising a clamp that never arrives. The condition path box offers the names your game actually declares and flags an unknown one — with a did-you-mean when there's a near match — instead of taking a misspelling without comment. And two smaller ones you were most likely to have hit yourself: a number field used to append rather than replace, so a field showing 6 read 612 after you typed 12; it now selects on focus and warns when the value is out of range. An auto-generated button label follows the move's settings as you change them, until you write your own.

Two placement bugs, both measured. A notice is no longer laid over the canvas region it refers to: one tester had to dismiss a panel offering to save his work off the device in order to reach the flow node that panel was covering, and dismissing that offer removes it permanently. Reproducing his case on the current build, three of eight state nodes sat under the notice; with the fix the canvas starts below it and none do. On a phone the same class of bug ran the other way — the entry notice's "Got it" button sampled 17% tappable, underneath the toolbar. Notices there are now one stack rather than separately positioned boxes at the same offset, and the same sampling gives 197 of 200 points. Separately, the Flow editor's inspector measured 1,634 pixels wide inside a 375-pixel viewport — the tester counted 53 of 59 controls off the glass, and our own re-measurement on a larger game counted 71 of 92. After the fix that same game measures 375, with 13 of the 92 still off-glass, all of them inside the graph canvas, which pans.

That last one was graded fixed a round ago and was not, which is worth explaining. The panel only blows out when nothing is selected — that is the state which draws the "add to game" tile list, whose one-line descriptions are set never to wrap. With something selected, the same surface measures 376. So the two rounds measured two different states, not two different builds; the stylesheet responsible has been in place since mid-July, before the first report of it.

What's not done. A phone can lay out pieces but still cannot author a rule — unchanged this round, and the entry notice still says so. The costed-graph-board building block still emits a definition the product's own schema rejects, and the tester who hit it was shown 33,124 characters of raw schema error as the only thing on offer — no plain reason, no route out. Restoring unsaved work can still return a game without the board that was drawn for it.

And the round's headline verdict finally moved. For the first time in eight rounds, two testers built a game from a blank editor and played it to a finish that named the right winner — 22 to 13, and 23 to 5. The failures above are what stopped most of the others getting there.

Try it → studio.ludira.io — "Build it myself" asks for no email.

Building a scoring game by clicking — and an ending that finally fires

Eight scripted playtesters spent the 2nd and 3rd building games entirely by hand — no AI, no spreadsheet import, no email, every rule clicked together. They are simulated: agents following written briefs, playing people who have never seen the editor, deliberately spread across different kinds of game so a problem two of them hit is a problem in the product, not in one game. Only one of the eight finished a playable game, and only because she stayed inside a template the product already ships. The moment a builder wanted a game of their own — points, a deck, an ending they wrote themselves — the click path dead-ended. Two of those dead ends are now gone.

You can build a scoring game by clicking now. A move can add a piece's own value to the player who makes it, a player carries a running score, and a game can end with "the player with the most — or the fewest — of a number wins" — none of which existed as a buildable option before. Proven end to end on the live product: a greedy player scored 14 to a meeker one's 7, and the game named the 14 as the winner — the score, not whoever happened to move last, which was the only kind of winner the tool could express before.

The building blocks compose. Drop a "shared pile" and a turn loop onto the table and you now get a game that actually ends: the turn loop adopts the ending the pile already declares, so a pile that empties finishes the game instead of freezing at zero with no winner and no next move. That freeze — a game you declared an ending for that still would not end — is the exact one five hand-builders hit last round.

Getting in and getting started are both signposted now. The front door has a plain "Build it myself" entry that asks for no email, alongside the "describe it to the AI" one — the editor is no longer something you reach only by closing the AI modal on a hunch. And the blank table finally has an "add a piece" prompt on it, instead of leaving you to hunt for where pieces live, buried under the Flow menu.

The play screen speaks in game words. The move log and the state chips used to print the engine's own variables — "game1: pile -1 → 4, lastTaker +1 → 2"; the same move now reads "pile down 1 (now 4)", with a "Show the internals" toggle for anyone who wants the raw version. The save pill tells the truth it was already keeping: it now says "Saved in this browser" — honest that a hand-built draft survives a reload, a closed tab, and (we tested) a 45-minute gap with nothing lost, and honest that it isn't in an account yet. A game with no ending is refused with "This game can't finish yet — nothing tells it when it's over" and an "Add an ending" button, instead of a line about failed structural validation. Unfamiliar words get a short plain-English gloss where they're used, and there's finally a "How Ludira works" door for the systematic version.

Four smaller correctness fixes rode along: a number field now replaces its value when you focus it instead of appending to it (typing 9 into a field showing 2 used to commit "29"), and it warns when a value runs past its own declared maximum; the move pickers now offer only legal choices instead of listing pieces already taken; and on a phone the editor's Play button is no longer hidden under the AI button, so tapping Play actually plays.

What's not done: not every mechanic is buildable by clicking yet — a move that slides a piece from one shared space to another still has no building block, so a game whose one rule is exactly that still can't be built by hand. A phone-only builder can lay out pieces but still can't author a rule — rule logic is read-only on a phone, with no "finish on a desktop" handoff. And your work is saved in this browser only, not to an account: it survives leaving and coming back on the same machine, but it isn't portable to another device and there is still no shareable draft link.

Try it → studio.ludira.io — pick "Build it myself", add a piece, and build a game that keeps score and ends. No account, no email.

Ten scripted playtesters, and what they broke

Ten personas went at the live product on the 27th — five describing a game to the AI, five building one by hand in the editor, four of them on a phone and nothing else. They are simulated: agents following written briefs, playing people who have never seen this before, not human beta testers. The result is one sentence. Not one of the ten described or built a game, played it, and reached a credible ending.

Two failures account for most of that, and both are ours. The editor has no way to author a move — MOVES (0) names the concept, counts it, and offers no button, while components and conditions each have one. All five editor personas hit that from five different directions. And four of five generated games never produced a working lifecycle: one would not initialise, one was dead on arrival at the seat count the room itself defaulted to, one deadlocked in round one, and one was trivially won at the end of round one because an ending condition was already true at setup.

The pattern underneath is the worse half. The product repeatedly detects a failure and then suppresses, misroutes or contradicts its own knowledge. Every editor persona who pressed Play was refused with "the posted definition failed structural validation" and nothing else, while the editor's own Rules panel was printing the named problem in plain English about six centimetres away on the same screen. A mail titled "Your Ludira game is ready" carried a game the engine then refused to set up at three different seat counts. The three controls where a rule can actually be written were rendered at zero opacity; both testers who found them did it by reading the page source.

What held up: the first genuine two-human session in five rounds — eight moves across two independent browsers, zero divergence, hidden hands correct every time, and a guest who reloaded mid-game back in her own seat with her own cards in about nine seconds. The CSV importer, the plain-English move refusals, per-seat secrecy and crash recovery were each praised unasked, several times by testers who left angry about something else. One failure in that session: the host was never told the guest had arrived.

Shipped the same night, live now. The Play refusal names what the game is still missing — no moves, no turn loop, no ending — in the toolbox's own vocabulary, says what was kept, and gives you a button to where each one is defined. Flow, Phases and Board are permanent labelled buttons instead of invisible ones. Bare felt, Escape and a Done button all get you out of a selection, which had been a trap that ended the session. The phone toolbar wraps to two rows with nothing off the glass, and says up front what a phone can and cannot do here. Piece numbers render where the decision is made, and a missing value reads as missing instead of silently as zero. And the host sees a friend take a seat.

Landing this week: the playability check stops being decoration. A game the probe could not play gets its own honest mail instead of "ready", an emailed draft shows the verdict when you open it, and a clean run is confirmed out loud rather than by silence. Alongside it, the first way to author a move in the editor — four archetypes (claim something, move a piece, adjust a number, pass), each a form over your own components, piles and numbers rather than free text.

What's not done: those four archetypes remember nothing between turns, so a full follow-suit rule is still out of reach. A guest who joins mid-round still sees no history from before she arrived — each client rebuilds the log from what it was sent, so that one needs server work. And the round's headline verdict stands until a later round overturns it: the path from an idea to a finished game has not been walked end to end by anyone yet.

Try it → studio.ludira.io — no account needed. If it refuses to play your game, it will now tell you what it is missing.

Shuffling is part of the rules language now, and the seed never reaches a player

Until this week the rules language had no randomness in it at all — the instructions we send the model said so in as many words, so a deck you shuffle and draw from was simply outside what anyone could describe. Two verbs are in the language now: shuffle a pile, and draw a number of pieces from one place to another. A game that uses them is still only a rules document, with no program code behind it. The shuffle order comes from a 128-bit seed and a position held in the game's own state, so the same game replayed from its move log deals the same cards — and a state that was never seeded fails loudly instead of the engine quietly inventing one.

The harder half was making hidden information actually hidden. A game could already declare a piece of state secret, and that declaration was being ignored on every live path: nothing read it, so the moment a real deck existed every player would have been sent the entire draw order along with their own hand. Secrecy is now worked out at the single point where the server decides what each player may see, which means a future feature cannot forget to opt in — there is nothing to opt into. Proven over two real connections in both directions, with a control that fails when the fix is reverted, and every existing sample game's board comes back byte-for-byte identical, so nothing became hidden that used to be visible. The seed itself is stripped from every player's view before anything else runs; a seed on the wire would undo every shuffle in the game.

The proof is a game rather than a test: a 36-card trick-taker, shuffled, nine cards dealt to each of four seats, written entirely as rules. Six different seeds played themselves to completion with an independent referee checking each result, and a four-seat game over the real live connection finished with the deck absent from every player's frames and each hand reaching only the seat holding it.

What's not done: dice. Rolling, and letting a game say "you can see how many cards are in my hand but not which ones", are the next two pieces and neither is built. The move picker also still lists all 36 cards instead of the ones you are holding — the server refuses an illegal pick, which is correct and still a poor way to play a card game.

Try it → studio.ludira.io — describe a game with a deck to shuffle and draw from. No account needed; dice are still the one thing to avoid asking for.

Five ways the product got between a designer and their own game

With generation working again, a second round of scripted playtests could finally do the thing the first round never could: arrive with a game of your own, already drafted, and try to get it running. Five batches of fixes came out of it, all shipped the same day. The headline defect was ours, not the model's — a game generated from a description like "act on the space you're standing on" was unplayable from its first move, and the bug was in the engine, in three separate places. A declared reference to a piece was seeded as a plain name; the write path then stored a name over a correctly-made reference; and writing to anything nested deeper than two steps had no expressible form at all, so half of "act on where you are" could not be said. All three are fixed narrowly, driven by the declared type, with a guard-rail test pinning that an ordinary word assigned to an ordinary field still stays an ordinary word.

Three fixes were about telling the truth. A move that threw an error inside a game's own rules used to close the connection and take the session down with it — now it sends one plain sentence, the table stays up, and there is a reconnect control. A generated game that nobody can finish is reported as the failure it is instead of a footnote: playable, partly playable, unplayable, or unverified, where an unplayable result buys exactly one repair attempt through the same validation gate every generation passes and is kept only if the verdict strictly improves — either way you get the blunt sentence. And no customer should ever be shown the name of an internal configuration setting; the pay button used to print one. Five leaks of that shape are closed, plus a second layer in the browser that renders our own wording on a checkout error and never the server's text.

The rest was the path from a generated game to people actually playing it. Starting a room with bots no longer destroys the invite link — a started room's empty seats are bot seats, and someone arriving on the link takes one over mid-game, verified against production rather than assumed. An arrival on a link never falls through to an unrelated card again. Every successful generation now creates a draft and hands back a permanent studio link to it, which no flow had ever offered. One-seat solo rooms exist and start the moment they are created, proven by playing a game of Nim to a real finish over the live connection. And spreadsheet import — which had been keeping every column, silently discarding every row, and reporting success by not complaining — now turns each row into a real piece on the table, with cards rendering as cards.

Two capacity and money fixes underneath. The whole product had been capped at two people generating at once; that cap is now eight, argued against the two real ceilings rather than guessed, with a separate smaller budget for the engine's own playability checks so that bot self-play can never starve live games. Arrivals past the cap queue for fifteen seconds before an honest busy answer with a retry time, and a test pins that being refused for capacity never consumes your free try. The spend report now counts failed calls, which are the expensive ones — it can say how much of what was spent bought nothing. And refunds, which were already real on every failing path, are finally audible: the failure email names the free try it gave back, and the front door stops claiming a used-up try after a refunded one.

What's not done: the repair round for an unplayable game is wall-clock aware, and on the wait-for-it path it rarely has room to fire. The one generation anyone has actually measured on the production box took 146 seconds, and a repair that cannot finish inside the remaining budget is skipped and never billed rather than run into a dropped connection. It earns its keep on the email-me-the-result path until generations get faster.

Try it → studio.ludira.io — describe a game, then take it to a real table and send the link to someone. No account, and the first generation is free.

Three failures that made no sound, and a phone that finally buzzed

The one step the notifications entry from the 22nd could not prove is proven: during a live two-player room test, real push notifications arrived on a real iPhone. Browser subscription, signed request, push service, phone — end to end on hardware, not inferred from a log.

That test is also what exposed the first of three defects that turned out to be the same defect: code that stays silent exactly when it matters. The notification module logged nothing at all, so a push that was never attempted and a push that failed at the push service produced byte-identical output — silence. That silence produced a confidently stated wrong diagnosis, twice, from evidence that could not support either reading. It now has one invariant: it is never silent. Adding those lines turned up a leak already in progress — every failed push had been writing the device's own push address into the production log, and that address is a capability, so anyone who could read the log could notify that device. It is no longer handed to the logging path at all, which closes the leak by construction rather than by remembering not to print it.

The second: a generation that times out is the most expensive failure we have — you wait out the whole budget and get nothing — and it was the only exit on its route that wrote nothing down, with the same hole in two sibling paths. All three now write the same marker, so one search covers them, and timeouts alert on a count accumulated over time rather than on the first slow prompt, because paging on every one teaches the reader to mute the channel, which is this same problem by another road. The third: a table that had dead-ended looked like it was still thinking. A scripted playtester sat in front of one for four minutes waiting on a bot that had already moved, and four separate layers each had the information without saying anything, leaving a two-player guess to cheerfully name the other seat as the current player for as long as anyone cared to wait. A dead-ended position is now reported once with the details, and the player is told the game cannot continue instead of watching a table pretend.

The next morning, generation failed on every real prompt, and the cause was arithmetic nobody had done. The model was never told how hard to think, so it reasoned at full effort — and reasoning spends the same output budget as the answer, so on a realistic description it used all 16,384 tokens thinking and emitted no text at all. Underneath sat two constants that could never both be satisfied: the socket timeout bought roughly 11,500 tokens of output while the request asked for 16,384. Any generation that filled its budget had been doomed since the day both numbers were written, and nothing checked. Effort is now sent explicitly and defaults to medium — measured, not assumed: across five prompts validity was identical, with 37% fewer output tokens and 39% less wall time. The retry no longer fires on a timeout, where it could only burn the rest of the budget in silence, and the route streams, so the timeout bounds the response headers instead of the whole answer. The guard is a test asserting those two constants stay mutually satisfiable; it failed the moment it was written, which is how the socket timeout came to be 200 seconds rather than 120. A four-sentence tic-tac-toe prompt had been taking 116.8 seconds, about three seconds under the timeout we shipped. A realistic prompt now finishes in 72.1 seconds at high effort and 42.9 at medium.

AI generation was down in production, and nothing told us

A round of scripted user testing walked straight into the headline feature being dead: the server's API credit with the model vendor had run out, and the vendor's own billing error was rendered through to the user verbatim — "Your credit balance is too low… purchase credits", with an HTTP code and a request id attached. It reads as Ludira demanding money seconds after offering a free generation. The failure also burned the user's one free try. Nothing alerted anyone: the account balance was the only ceiling, so hitting zero was the first notification. The balance has since been topped up and generation is verified working again live — 200, 7.8 seconds, three of three playable.

So alerting got built the same day, two detectors because neither covers the other's blind spot. A balance canary makes one deliberately tiny billable call — it has to be a real charge, because the vendor's free token-counting endpoint answers 200 even with an exhausted balance and would have missed this exact outage. A log watcher greps for failed model calls, costs nothing, and covers provider outages, auth failures and plain bugs. A sustained outage pings once rather than every cycle: it alerts on working→broken, on a change of failure signature, on a six-hour reminder, and on recovery. Every one of those paths was fired for real before it was called done — bad key, a real credit-exhaustion line, a deliberately broken checker. It costs about eight cents a month to run.

What's not done: the server's log output is block-buffered, so the log watcher can't see lines the app hasn't flushed yet — which blunts half of what just shipped. The one-line fix needs a restart and is queued. The balance canary is unaffected, so credit exhaustion is still caught within about twenty minutes.

We were paying for the same 14,594 tokens on every AI call

Every AI generation sends the model a fixed instruction prefix — the rules format, the constraints, the examples. It is byte-identical on every request, it is 14,594 tokens long, and it was being billed in full every single time. Our own pricing note had already justified the 5-credit charge as reflecting the cached cost of that prefix. The assumption was written down and never implemented.

It is cached now: the prefix goes as its own block marked cacheable, with a test that locks in that those bytes stay identical across different user prompts, for both generating a game and patching one. Measured against the live API: the first call reads 0 cached tokens, the second reads 14,594. Input cost drops 89.2% on a cache read against a one-off 24.8% surcharge to write it, so it pays for itself on the first read; the whole call comes out 65.6% cheaper. A three-attempt generation went from $0.0884 to $0.0432. Token usage is now parsed and logged on every call — the client had been discarding those numbers entirely, which is why nobody could see any of this before.

What's not done: the cache hit is proven against the live API, not on the production box. The second generation there was refused by the anonymous free-try gate before it could make the call — itself a real bug the same round of testing found. So the mechanism is proven; production-side proof is not.

Push notifications: the whole feature was built, and had never once worked

The turn-notification chain — the bell in the app, the service worker, the encryption, the pruning of dead subscriptions — had been built and tested weeks ago. Turning it on in production surfaced a gap no amount of code review would have found, because it wasn't in the code: the reverse proxy on demo.ludira.io had no route for the notification endpoints at all. Every call the bell ever made had been 404ing at nginx. The feature had never worked in production, regardless of how the app itself was configured.

Now wired: a stable signing key pair generated on the server itself (the private half never appeared in any transcript — only the public key was ever printed), subscriptions persisted to disk instead of memory, and the missing nginx route added. Restart stability was proven twice over — no ephemeral-key warnings, and the public key comes back byte-identical across restarts.

Delivery is proven as far as this environment allows. Browser notification permission is hard-blocked in the sandboxed browser used for verification, so instead a real, crypto-valid subscription pointing at Google's production push service was registered against a real production room. When a real move passed the turn to that seat, the subscription was pruned from the persistence file — and the only code path that prunes one is a 404 or 410 coming back from the push service itself, with two control subscriptions left untouched. That proves the chain end to end: turn detection, encryption, signing, and a real outbound request to Google's push infrastructure.

What's not done: nobody has yet watched a notification actually appear on a real phone. That is the one step this setup can't prove, and it's queued as a manual test.

Four flaky tests later, the build is honest again

For a stretch, CI was failing on roughly half of all pushes while every local run stayed green — which makes a test suite worse than useless, because you learn to ignore it. Four separate causes, found one at a time by watching the real CI runs instead of trusting local ones. Two of them were real bugs. A move's bookkeeping ran after the move had already been broadcast to connected players, so a client reacting to that broadcast could race it and corrupt a seat's idle-strike count — a genuine concurrency bug that a contended shared runner hits far more often than a quiet laptop. And the smoke-test job ran an unqualified build command that matched three separate runnable subprojects, one of which exits immediately when there's no API key; the build aborts on the first failure, so depending on scheduling order the server under test sometimes never booted at all.

The other two were in the tests themselves. One polled an assertion after the connections it was testing had already closed, so on a slow runner the code correctly saw a disconnected player and did the thing the test said it shouldn't. The last one, found this week, was a timing test that failed its own precondition on a fast runner — its fixed-size workload finished too quickly to measure, tripping the guard that exists so the test fails loudly instead of passing vacuously. It now sizes its own workload until there is something real to measure.

All four are confirmed fixed by real CI runs coming back green, not by local runs alone.

Pieces look like pieces — and looking at them found a real bug

Until now a game piece in the editor was mostly its id: a raw instance string as the headline. Now every piece renders as a colored disc with a role glyph inside it — a person for a player, a token, a board, a component — with the piece's type as the headline and the instance id demoted to a small mono sub-label. The color is derived from the type name, so every instance of a type matches without anyone picking colors, and an owner is drawn as a ring around the disc rather than a fill, so the type color still reads. Same treatment in five places across the editor, verified live at both phone and desktop widths.

Verifying it is what turned up the bug. Composing a game from an alternating turn loop, a slot board and token stashes — but without the two-player piece set — produced a game that looked fine and then returned a 500 the moment you started a live room: "expected a number, got StrValue(v=nextMark)". The piece set was the only thing declaring the turn counter that the turn loop's setup then read, and nothing enforced that coupling. Every playthrough anyone had verified before happened to include the piece set, which is exactly why this survived so long. The turn loop now declares what it needs itself, proven with a real before and after against the live server: the old shape reproduces the exact 500, the fixed shape starts a room and plays.

What's not done: one related gap is documented and not fixed — a resource pile combined with the turn loop still needs a different flow shape than the turn loop provides. And two layout estimators (multi-group entity trays, and rail wrapping) both undercount, which shows up as minor region overlap in dense games. Recorded, not quietly dropped.

The editor on a phone: one toolbar row, and a button nobody could tap

Measured first, at a real 375×812 phone viewport, before changing anything: the editor's toolbar wrapped onto four rows and ate 219 pixels — 27% of the screen. And a separate, worse problem: the sidebar's own collapse toggle was completely unreachable by a real tap. Both it and the toolbar sat absolutely positioned at the top of the screen, and the toolbar won across its full height, so the tap never landed on the button. Not a styling nit — the control was simply gone on a phone.

The toolbar now collapses to a single ~56px row at narrow widths: the title ellipsizes, the save-state caption drops, and Import, the account bar, Edit flow, Phases and Edit board fold into the existing ⋯ menu rather than a second menu invented for the occasion. A shared height variable now positions the sidebar below the toolbar instead of underneath it, which fixes the unreachable toggle at its root. The sidebar starts collapsed on a phone, auto-fit stops tucking board content behind the toolbar, and the play screen's status bar got the same single-row treatment.

Two bugs the build caught in its own verification and fixed before reporting: an overflow guard silently clipped the popover menus (CSS couples the two overflow axes, so constraining one constrains the other), and a min-width of zero on the title cluster let the neighboring clusters claim the whole row, shrinking the game title to about 19 pixels. Live verification passed all six checks, including a genuine tap-driven sidebar open and close — 41px collapsed, 261px open with real content visible, back to 41px, not just a class name flipping — plus a desktop regression check at 1280px.

Try it → studio.ludira.io on a phone: one toolbar row, everything else behind the ⋯ menu. No account needed.

Rules now compose from the real pieces on the table — no flow chart required

Straightforward games no longer need a separate rules-logic screen at all. Drop a board shape, a stash of pieces for each player, and a placement rule onto the table, and the win condition, turn order, and legal moves assemble themselves from those real pieces — the same click-to-place board you'd get playing the finished game, live while you're still building it. A tic-tac-toe-style game can be put together and played this way start to finish without ever opening a flow chart.

Games complex enough to actually need one — named phases, a forced turn sequence, an interrupt rule — still get the full flow editor, plus a new compact panel that explains in plain language exactly why (our large economic test game shows: "named phases beyond Setup/Play/Won/Drawn; phase entry/exit effects; interrupts; a forced per-actor decision sequence"). It sits next to the flow editor, not in place of it.

This shipped in stages, and playing a real multi-piece game through the live product — not just trusting the build — caught four real bugs before it was called done: duplicate piece IDs, a placement rule that looked wired up but wasn't actually connected to play, a confusing-but-harmless UI label, and a dead end when editing a player's starting pieces at design time. All four fixed, redeployed, and reconfirmed by playing it again.

Try it → studio.ludira.io opens straight onto the table; the sidebar's building blocks compose a playable game without an account and without spending an AI generation.

Rethinking how a game's rules get built, before building it

The flow-chart editor works, but for a lot of games it's more machinery than the game actually needs — a separate screen you have to think in, disconnected from the board you're actually designing. The alternative: build the rules by wiring the real pieces — the board's cells, a player's stash, a deck — directly where they already sit on the table.

Before committing to it, the idea got stress-tested against the hardest game in the stable — an economic strategy game with auctions, a resource market, and a five-phase round structure — to make sure it wouldn't just work for simple games and fall apart on a real one. It held up. A first playable version (compose a game, then actually play it, both on the table) got built, played, and approved before any of it touched the real product.

The editor now opens as the game unpacked on a table

The biggest change yet to how you actually build a game: the editor no longer opens on a list of rules, it opens on a table. The board renders through the same real renderer the play screen uses, and every part of the game — markets, board, player seats, rulebook — sits on the table like it would in real life. Drag any of them wherever you want, and your layout saves with the game.

Four fast rounds of building against direct feedback landed two rules that make it feel like a real table instead of a webpage: nothing on it ever scrolls inside itself — pieces and boxes grow to fit what's in them, and you pan the table around them instead — and only one thing on the whole screen zooms, the table itself (scroll to zoom toward your cursor, drag the felt to pan, a Fit button to reset). That's the editor live on studio.ludira.io now, not a preview.

Two smaller but real fixes underneath: a public endpoint's timeout turned out to be cosmetic on closer, adversarial inspection — the check existed but nothing was actually reading it — and got rebuilt so it genuinely cuts off long-running work. And the crash-recovery safety net for unsaved edits, which shipped this week, already caught its first real save during an actual crash.

The studio is a real, live product now — and it can meter credits

The studio moved off its internal address to a real one, studio.ludira.io, wearing a proper Ludira shell throughout: the loop mark, the Fraunces wordmark, fonts self-hosted rather than pulled from a third-party CDN (consistent with the no-tracking promise), your account, plan, and credit balance visible everywhere in the app instead of one screen, and the game's name and save state right in the header.

Credits went from tracked-but-unused to actually working end to end: every free account starts with 25, AI generations spend them, running out blocks generation cleanly with an upgrade prompt instead of a confusing error, and anything that fails or times out refunds automatically. To be upfront: real payments aren't live yet — checkout stays disabled until the payment provider's review finishes — so credits are the whole loop for now, no card gets charged.

Also this week: a Refund & Cancellation policy is live at /refunds, and the front page got rebuilt around what Ludira actually is today — the studio is the headline with a working link straight into it, the demo moved to supporting proof, and contact and company details are in the footer.

Games now come with their pieces, and the paperwork got real

Until now a game's rules and its actual contents — how many pieces, which spaces are on the board, how much of each resource — lived apart, which was a real gap. Games can now declare their real "box contents" as part of the game definition itself, editable right in the studio. That one change let our large economic test game run a genuine self-play test end to end for the first time, real pieces and board data included — which matters because the automated checks that keep the engine honest now run against something much closer to a real game instead of a bare rules skeleton.

The AI co-editor from the day before also got a real quality check against that same full game: five separate real edit requests, and every single one came back valid, actually playable, and faithful to what was asked. Separately, on the boring-but-real side: Terms & Conditions and a Privacy Policy are now live (ahead of formal legal review, which both pages say plainly), pricing structure groundwork moved forward, and the legal/privacy/abuse contact addresses are real, working inboxes now instead of placeholders.

The builder studio took shape

This week the game-editing side of Ludira went from "hand-edit a data file" to an actual visual tool. You can now draw a game's rules on a canvas — add and remove states, connect them into transitions, drop in game pieces — and click rule conditions together instead of typing them. A separate board editor lets you draw the board itself as a graph and give each connection between spaces its own cost.

The biggest piece: an AI co-editor you can talk to in plain language — describe the change you want — and it proposes a real diff for you to review before anything is applied. Every proposed change is checked and played out by the engine before it's even shown to you, so a broken change never reaches the review screen. Also new: push a rule change to a live playtest and every connected player's board updates instantly, a proper tabbed workspace with pinnable panels, and enough of it works on a phone screen for small edits on the go.

What's not done: the studio itself isn't public yet — it's an internal tool for now, not something a visiting creator can sign into and use.

The flagship game got played for real — and won

The economic strategy game from the acid test (auctions, a resource market, a five-phase round structure) had already passed automated self-play, but nobody had sat down and actually played it by hand yet. That happened this week: a full game, start to finish, ending on a real winner's screen. Playing it surfaced three real bugs along the way — a spending check that didn't account for the true cost of a purchase, a bot that could freeze mid-turn without recovering, and a finished game whose screen kept saying "waiting…" after the game had already ended. All three were found by playing, fixed, and reverified.

What's not done: this win was solo against bots, not yet a full table of real people at once — see the room feature below for that.

A zero-signup demo, now with rooms for two

The demo from last time now has a real public address: five public-domain classics, playable against a bot in the browser, no account, no email field anywhere in the flow.

New this week: rooms. Start a game, copy a share link, send it to a friend — they join and you play live, the board updating for both of you over a real-time connection, every move still validated server-side by the same rules engine as everything else. If nobody joins in time, a bot fills the seat so the game still runs.

What's not done: the room flow is new and has only been played by a couple of people so far, not yet stress-tested with a crowd.

Try it → demo.ludira.io — pick a game, then "Play with friends" for the share link. No account, no email.

A zero-signup demo exists

Five public-domain classics — tic-tac-toe, connect four (on a compact 5×4 grid), chomp, nim, and three men's morris — are now playable in the browser against a bot, with every move validated server-side by the same rules engine that runs everything else. No account, no email field anywhere in the flow, no third-party analytics; the only telemetry is a short fixed list of named counters stored on our own server.

What's not done: the demo's public web address isn't live yet, so the "Play the demo" button on the front page stays disabled until it is.

The engine passed its acid test

The whole premise of Ludira is that game rules are data, not code. The acid test: define a full economic strategy game — auctions with bidding and passing, a resource market, score-based turn order, a five-phase round structure — purely as declarative rules, with zero custom program code, and have the stock engine run it to a real winner. It does, now, verified by self-play across a dozen random seeds with the final scoring cross-checked by an independent recomputation from the raw move log.

Getting there wasn't clean on the first pass: adversarial review caught the fuel economy being quietly decoupled from scoring, and a tie-breaking inequivalence. Both fixed and re-verified. One disclosed simplification remains (a uniform fuel cost per city) and is queued as future work, not glossed over.

Ludira has a name and a face

The project (long known by its working repo name) is now Ludira. The name was confirmed after checking domains and trademark risk, not just picked for sound. The mark is a "live rule loop": a game-round circle with a teal live segment, a coral patch segment, and an amber bolt for the moment a rule changes mid-game — which is the one thing this tool is really about. Display type is Fraunces; the amber cue is reused across the product as the "a rule just changed, live" signal.

This site went live at ludira.io the same day.