v0.6.1 (2026-07-31)
The v0.6.1 release implements numerous fixes, largely to the validator scripts that were first shipped in v0.6.0.
v0.6.0 modified test case specifications and introduced a set of validator scripts. These scripts allowed for testing specific scenarios to ensure that an implementation correctly implements the spec, but multiple scripts were either buggy or were too specific to the reference implementation and would fail on other spec-compliant implementations.
As part of preparing v0.6.1, a more thorough pass was done to test the validators against various other implementations and against deliberate changes intended to trip the validators. This required fixing a massive number of issues with the original validators.
Validator checks were also expanded. The original validators erroneously did not validate audio, which increased the manual review surface. These are now verified programmatically. The review item requirement was always a pass/fail check, which is fully verifiable programmatically because it depends on whether audio is played, not the quality of the audio the implementation uses.
Validators were also expanded to cover a frequent failure mode observed after the debug API addition to the specs. The debug API requires models to implement their game code differently to allow the API to be implemented, which has frequently resulted in models failing to account for their own debug API code and shipping a game that doesn’t even run. New checks were added so that implementations broken in this way are caught automatically.
This remains pre-1.0 software.
Features
Section titled “Features”Validation scripts can skip the journey to the evidence
Section titled “Validation scripts can skip the journey to the evidence”A scripted item runs twice: an instant validate pass that decides the verdict,
and a real-time record pass that films the clip a reviewer watches. advance
and until are exact steps in the first and real wall-clock waits in the second,
which is what makes a clip show the game at the speed it actually runs.
But plenty of scenarios have to travel a long way before the behavior under test can happen at all — a unit walks the width of the floor before it can leak, a wave plays itself out before it can pay a bonus, a cascade runs for four seconds before the prompt appears. Filming that is not evidence; it is the journey to the evidence, and it was most of the clip, most of the run time, and most of the stored bytes.
The runtime gains skip(amount) and skipUntil(predicate): the same real
simulation, landing in the same state, advanced with an exact step in both
passes — no wall clock, no filming budget, no footage. An item skips the approach
and then advances the part a reviewer needs to watch, so the clip opens on the
behavior instead of a minute before it. Unlike advance, skip is legal in
arrange, which is where most of it belongs; used in act it hands the build’s own
clock back afterwards exactly as reset does. Verdicts are unaffected — the
validate pass was always instant — so this is purely about what the recording
contains.
Audio is checked cue by cue
Section titled “Audio is checked cue by cue”Every non-experimental game case carried a single human-reviewed item along the lines of “distinct synthesized cues for the mandated events”. That is one point for between four and fourteen separate requirements, and no way to tell which one a build missed.
Each is now one scripted item per spec-mandated cue (plus the music bed), driven
through the debug API and confirmed against the Web Audio sources the build starts,
read through the driver’s api.audio() probe. The catalog went from 9 per-cue audio
checks to 86, across eleven cases: Carom (4), Shatter (5), Fathom (7), Floe (8),
Spectra (9), Wireworm (9), Meltdown (8), Coil (4), Arc Foundry (11), Deepcore (14),
and Valence (7).
Two cases needed the baseline to catch up with the checks: Meltdown’s reference implementation had no audio at all and gained a synthesized-audio subsystem wired at each cue’s event site, and Floe’s defined its splash and crush cues but never called them.
The properties that are genuinely subjective — that the cues are distinct, that mute works, that nothing plays before the player interacts — deliberately stay human-reviewed items.
Arming audio with a real browser gesture
Section titled “Arming audio with a real browser gesture”The audio checks have to unlock audio first, because a game must not autoplay before
the player interacts. Arming went through the debug API’s press, which a build is
free to implement as a purely logical input path — the contract only asks that
injected input reach the same game handling the keyboard does. A build that also
creates or resumes its AudioContext only from a real DOM interaction — equally
conformant — therefore never unlocked its audio under the probe: no source was ever
started, so every audio check failed on a build that played its cues correctly for a
real player.
The browser driver gains two primitives that bypass the debug API entirely:
api.userKey(code), a genuine browser-trusted key tap through Chromium’s own
input pipeline, and api.userClick(x, y), its pointer counterpart, for a build
that takes its first interaction from a click rather than a key. Both fire the DOM
events a build’s own listeners catch and both satisfy Chromium’s autoplay gesture
requirement. Cases arm with a neutral key and an inert corner click, so a
conformant build is armed however it takes its first interaction.
Every case checks that the game advances on its own frame loop
Section titled “Every case checks that the game advances on its own frame loop”Every scripted item drives the simulation through step(). A build whose own
requestAnimationFrame loop never runs — or that only ticks when the debug API asks
it to — passes the entire suite while a player sees a completely frozen board.
Nothing checked the one thing a player notices first.
Eleven cases now carry an item that measures the clock the build boots with:
posed with control operations only, no reset and no step (both hand the clock
over to the driver), and a real settle in arrange before the runtime takes it. Its
media is two stills rather than a clip, because the record pass turns autoStep on
for act and a filmed act animates even for a frozen build — so a frozen build’s
two stills come out pixel-identical.
Each case needed its own witness that moves unbidden: a served ball, a patrolling predator, lane traffic, a spawned surge unit, drifting rocks, a diving drone, the level banner giving way, flowing matter. Deepcore has nothing that moves on its own, so it holds a direction the way a player does and additionally reads the autoStep flag its snapshot exposes. Coil’s window is one second rather than the usual two, because its snake runs out of board at ~1.75 s and ends the round being measured.
The jam prompts, declanked
Section titled “The jam prompts, declanked”Every game-jam brief was rewritten in a plainer, flatter voice. The old prompts argued their theme at length in the project’s own prose register; the new ones read like a jam brief: the theme in a sentence, the fixed requirements as short labeled bullets, and the rest of the page spent on the things a model actually needs — the workspace and its git contract, the time budget, the asset binaries, and what the container provides.
Concretely, across all eight briefs: bolding is cut back to the genuinely
load-bearing words (the requirement labels, the time budget, the base-path rule) and
the mid-sentence em-dash asides are unpicked; each brief now states outright that
the jam’s name is the theme and may not be used as the game’s title; and
“Verify before you finish” becomes “Verifying your work”, which reports what the
container actually provides (project-local Playwright and Chromium, network access,
the --no-sandbox requirement) and what is judged, instead of prescribing a
verification routine. The competitive framing — that the entry is judged against
other contestants’ entries on fun, playtime and replayability, and aesthetics and
polish — is now stated identically in every brief.
The first jam — added in v0.6.0 as Dead Man’s Switch — is renamed 🚂
Locomotivation, taking the name freed up by the deletion below. It is renamed
outright: the theme, the slug (locomotivation), the catalog name, and the folder
all move together. Nothing is orphaned by that, because the jam has no runs recorded
against it.
Locomotivation is no longer a test case
Section titled “Locomotivation is no longer a test case”The Locomotivation full-stack case — the ¾-overhead rail-yard hauling dash added in v0.6.0 — is deleted, along with its reference implementation, its 366 files, and its validation tree. It had no runs recorded against it.
Repeated jam runs never saw the earlier entries
Section titled “Repeated jam runs never saw the earlier entries”v0.6.0 shipped a mechanism for briefing a jam run with the READMEs of the same model’s earlier entries, so it would build something distinct rather than a near-copy. A model that ran one jam three times produced near-copies each time, because the briefing never reached any of the runs. Two independent defects, either of which alone empties it:
- Parallel dispatch. The queue only held a job back for its harness’s
parallelism cap, so all three runs of one jam were claimed at once and each fetched
the prior READMEs before any of the others had finished — so every run got an empty
history and none carried the distinctness section. Jam runs of the same jam and
model now go one at a time: the claim pass refuses a
game-jamjob while another run of that (jam, model) occupies a slot, whichever harness either uses, and leaves it visiblypending. Other jams, other models, and every non-jam type are untouched and still run in parallel. Thejobrow gains atest_typecolumn, lifted at enqueue from the ingested manifest, for the queue to key off. - Harness-scoped history. The lookup matched
(jam, harness, model), so the same model under a second harness retold the same idea with a clean slate. It now matches(jam, model)across harnesses — what repeats a game is the model, not the tool driving it — and the prompt and seeded index drop their “with this harness” wording to match.
The briefing was also invisible after the fact: a run’s Inputs tab renders the case’s
authored inputs, which are identical for every run of a jam, so there was no way to
tell a run that was shown nothing from a mechanism that was not working. A run now
records the entries it was seeded with as the inputs they are —
RunRecord.gameJamPriorEntries carries each earlier run’s id, finish time, and its
README body — and the Inputs tab renders each README inline at the
previous-entries/entry-NN.md path the model read it at, in the same accordion as the
jam’s prompt and specs. A record therefore stands on its own: the text a run was
briefed with is on the run, not chased to an earlier one that may never have been
published.
The Web Audio probe never saw a sampled clip
Section titled “The Web Audio probe never saw a sampled clip”The driver’s audio probe wrapped AudioScheduledSourceNode.prototype.start,
assuming every source kind inherits it. Per the Web Audio spec
AudioBufferSourceNode.start(when, offset, duration) takes extra parameters, so
Chromium gives that subclass its own start that shadows the base — meaning the
probe never saw a decoded or sampled clip play, only plain oscillators and constant
sources. Every full-stack case plays its produced .wav files through
AudioBufferSourceNode, so their cues were invisible to the probe by construction.
The probe now wraps the base class and each concrete subclass unconditionally;
each wrap is a no-op where its target has no own start.
Relatedly, several cases read the source log immediately after the step that fired the cue, which races a build that queues its cues and plays them from the render loop — those builds reported silence. The reads now settle a frame at both ends of the measurement.
Checks that pinned the reference rather than the spec
Section titled “Checks that pinned the reference rather than the spec”The most common defect by far. A check asserted something only the case’s own reference implementation happens to do, so a build that satisfied the written spec failed anyway:
- Fathom — six checks: the first flare’s absolute time (the spec pins only the
gap), the one 5 px ring the reference’s amber orb is brightest at, a fixed 240-tick
wait for an interstitial the spec only calls “brief”, and a wrap-tunnel check reading
a
movingflag rather than ground covered. Earlier, three more defects had been failing twenty items on conformant builds — a “quiet the board” helper that actually armed a maze clear, two movement checks requiring a newest-key-wins tie-break no spec defines, and a tile-index comparison that failed a build integrating toy = 128.0000000000001. A graded gpt-5.5 run moved from 67 pass/28 fail to 87/8, with the remaining eight being real build defects. - Shatter — fourteen checks, each asserting a convention the case never picked:
livesread as an absolute where the specs call the field “ships in reserve”; facings compared by subtraction, so a build keeping its angle in[0, 2π)read as 2π of error on the very value the spec writes as “270 degrees”; a tap counted with no time elapsed, which only ever sees a build that fires insidepress; wave 1 sampled before it spawned; death sweeps expiring inside a build’s own pause; and a speed-cap plateau that is a band one drag-step wide. - Floe — the level-clear and bay awards pinned to an exact 80/180, which silently assumed where the bonus fish sat, when the fish picks its bay from the build’s own seeded generator; five bear checks sized to the reference’s own emerge delay, where the spec says only “after a short delay”; and a floe-drift check matching floes across a step by array index, which the snapshot contract does not promise.
- Valence — the hard map required to have three tracks and never cross (the spec says two or more), the curved map assumed to be the single-path one, a tower’s aim measured from a muzzle offset, and the kinetic bond bonus read off a hand-tuned window rather than one shot’s damage. Each now tests the property the spec pins, verified against three implementations and 36 mutants.
- Carom (new v2.0.1) — the gyre variant’s obstacles sway and rotate, so no
mid-field lane stays clear and no obstacle face stays axis-aligned, but several
common checks drove the ball as if the field were still. They relied on the
reference’s implicit obstacle-clock freeze-at-0, which the spec never pins; a new
pinObstaclesUprighthelper poses the clock explicitly (probed, so a no-op in base and multi). Andspin/moving-solo-airequired an absolute spin above 400, which only a near-top-speed swing reaches — the AI is deliberately slower than the human and eases off near the ball, so a gentler-but-conformant AI failed. It now reads spin against the paddle’s actual velocity. - Wireworm — the specs never said whether a foe’s
(x, y)is its sprite centre or its top-left corner, so every check that posed or sampled by tile silently tested the reference’s choice; the specs now fix it as the centre. Four more checks stopped asserting free choices (worms standing after a level clear, spawn-in invulnerability, a trail’s exact column, a corruptor’s absolute row). - Wireworm — the four items that turn on a worm reaching the cursor (the run
ending, its screen, its sting, and the respawn) posed a segment already overlapping
the cursor and then gave the build 6 ticks — 0.05 s — to notice. But
specs/progression.mdcosts a life when a segment reaches the cursor, and a build that tests for that where the reaching happens — inside its own worm tile step, once per 0.14 s — never saw a contact that had already happened before it looked. All four failed on a build that plays the collision perfectly by hand. The worm now walks the floor row into the cursor and each item waits for the outcome rather than a tick count.respawn-keeps-fieldadditionally read the cursor on the tick of the touch, where the same spec puts the re-centring after the pause — “briefly clear the board of the current worm(s) and foes, then respawn the cursor centered in the band … after a short pause” — so a build that does the re-centring when the cursor actually reappears read as one that never did it; the board-clearing half is still read at the touch, the cursor once the respawn has run. And its ±1 px bound failed a build that respawns 2 px below the midpoint of a 32 px strip, a precision a spec saying only “centered” never asked for; the vertical bound is now ±4 px, which still rules out the top and the floor. - Arc Foundry — maze length is now specified in tiles with a diagonal step counting √2, and solved on that same length, so a wall the route must round always raises the figure; a step count made a diagonal detour free.
- Cascade —
winning/detect-double-clickdrove the gesture as thedoubleClickoperation plus a trailingpointerUp, on the reasoning that a double-click ends with the pointer coming back up. But that operation is the whole gesture under the instrumentation contract, so the extra release was unpaired — no press before it, and no player can produce it. On the won screen a click deals a fresh game, and the specs let a build commit that click on either the press or the release, so the check failed every build that chose the release — while passing one that deals a fresh game mid-gesture and never plays the victory cascade at all. The item now drives a real browser double-click through a newapi.userDoubleClick(u, v), a third primitive alongsideuserKey/userClick, normalized over the game canvas the wayapi.pixelis. It is the only thing that reaches all three places a build may recognize the double — thedblclickevent, the second release, the second press — and so the only thing that can see the gesture’s own remaining events land on an already-won game. Of three graded builds, the two that played the gesture correctly moved from fail to pass, and the one that was passing now fails for a defect a player sees.
Checks that posed a world the build was entitled to read as over
Section titled “Checks that posed a world the build was entitled to read as over”A distinct and unusually destructive shape: the check poses a scenario by emptying the board, the build’s own specs define an empty board as a cleared state, and the build correctly advances — freezing or wiping the scenario mid-measurement, so dozens of unrelated items fail against a game that plays perfectly by hand.
- Valence — scenarios were posed in the opening build phase, which a build may
leave un-ticked; 48 items froze. A new
startScenariooperation opens a live, wave-less round that does not end on its own, and is itself graded, so a build without one fails by name rather than as dozens of unrelated verdicts. - Wireworm —
freshBoardleft no worm segments, whichspecs/worm.mddefines as a cleared level, so a build advanced on the first tick and 22 checks failed. It now poses a bystander worm the scenario replaces or ignores. - Spectra — an empty posed field is a cleared wave, so twelve items lost the live-wave screen: held movement and fire keys stopped answering and the flip key did nothing. A bystander drone now keeps the wave alive.
- Meltdown —
build()cancelled its held preview with Esc, a key bound three ways (cancel placement, else deselect, else pause). On a build whoseplaceTowerleaves nothing armed it paused the game insidearrange, and 15 heat, cooling, targeting, economy and audio checks then measured a world that never advanced.
Checks that could not fail
Section titled “Checks that could not fail”The mirror defect, found by mutation: an item that passed a build with the behavior it exists to verify deleted outright.
- Fathom (Kindle) — all six of the vision-circle variant’s own checks passed a
build with the mask deleted.
isDark(luminance < 26) cannot separate the two colors in question: flat fog reads ~7 and a remembered dim corridor ~15, so ground the build is visibly drawing passed as “hidden”. They now compare against the build’s own fog, sampled from a never-revealed tile, which is what the spec actually states. - Floe —
hunter/continuouscredited “the bear glided” to a single non-zero sample, which a bear that jitters one sub-pixel step and is dragged back every step satisfies while sitting frozen.hunter/stays-aheadandhunter/safe-baysmake negative claims, both trivially satisfied by a bear that cannot move at all — so a build with no working pursuit passed two items about pursuit being fair. Each now accumulates the bear’s real path length. - Meltdown — seven checks passed the defect they exist to catch:
flak-air-onlytook an else branch assertingtruewhen the Flak killed the Mote it was supposed to spare;bloom/splashaccepted two units damaged by separate shots;sealing/no-trapre-tested the never-seal rule on the same placement asno-seal; and five audio cues measured windows carrying other cues, so the source count grew with the cue under test deleted. - Cascade — the win-detection item drove the last card home through the
movecontrol op, exercising the rules layer only. A submitted build recognized a double-click on the secondpointerdown, so the win fired mid-gesture and that same gesture’spointeruphit itsscreen === "won" → newGame()branch and dealt a fresh game — the victory cascade cleared before it drew a frame, and the check passed. It is replaced by two items that drive the last card home by real gesture, from both piles a player can play it from, and snapshot again after 1.5 s of cascade. - Floe — the death checks swept for
phase === "dying", which the specs never defined; a build that resolves a death within the tick that caused it never reports it. They now read thelivesdecrement, and the specs gained a death pause so the enum value has something saying when it holds. - Arc Foundry —
combine-actions-onlyrequired an action set omittingcomborecipeunder a label expressly allowing it; a newbuild/explicit-set-folds-exactlycovers a claim nothing checked. - Meltdown — a new
targeting.fire-ratecovers an emitter that banks its idle shot timer and discharges it one shot per tick; a shot count alone cannot see it, because the burst trips the tower, so the smallest gap between consecutive shots is read too. - Wireworm —
respawn-keeps-fieldposed the cursor at the centre of the band and then asserted that the respawn put it there, so “respawns centered” was already true before the touch and passed whether or not the build ever moved it — including a build that leaves the cursor exactly where the player last had it. The worm now walks into a cursor parked off-centre, which is the only arrangement in which the assertion says anything. - Floe — a new
instrumentation/manual-clockitem scores the debug API’s clock contract itself, which nothing checked. A build that resumes its own clock inside a control op plays perfectly for a person and answers every declared call, but no scripted scenario is reproducible — which surfaces as other checks reporting defects that are not there, with nothing to point at.
Checks that measured the wrong thing
Section titled “Checks that measured the wrong thing”- Meltdown —
trip.returns-coldread heat on the first step the tower is back online, which on a build that resolves the cooldown before firing is one shot’s self-heat;refund.partialread the balance after a sale as if it were the refund;sealing.no-trapframed a 2×2 pocket, so the unit sat next to the tile under test and occupancy explained the refusal. Separately, most firing items posed their emitter beside the rows a left-vent unit enters on — a routespecs/playfield.mdnever pins, since with diagonal steps costing the same as orthogonal ones, climbing to the top of the floor and back is exactly as short as crossing straight. Those items now build the lane with sinks rather than assume it. - Spectra —
flux-oscillatesandprism-shell-then-corereadband, which the snapshot contract documents as the stored band; they now readeffectiveBandandcoreBand, the fields that are pinned.challenge-alternatingclustered drones by first appearance in the snapshot, reading a build that queues its flyover off-screen as one group.dive-bends-firespassed any lateral drift, and now runs the dive from both sides. - Deepcore — pixel sampling read clamped edge pixels off-stage and a fixed settle
read stale frames, so the color items reported meaningless zeros and
gas-hiddenpassed on a canvas that had painted nothing. The camera-lead bounds were one-sided, so an off-screen miner cleared them. - Arc Foundry — combat items measured inside half a second, shorter than a
Capacitor’s own cadence, and left targeting on the default
firstwhile the level’s wave 1 walked the same corridor, so a tower firing perfectly well was firing at a wave unit.critread the multiplier off a projectile, requiring the roll at launch rather than on impact. - Coil —
api.advanceis an exact step in the validate pass but a real-time wait in the record pass, so a control op issued right after one could cancel the tick it was waiting on. Both now hold onapi.until, which spends nothing in the validate pass. - Wireworm — the four items that shoot a foe dead (
glitch-one-bolt,corruptor-one-bolt,dropper-two-bolts’s second bolt, andaudio.foe) read the foe list on the tick the BOLT was consumed, which is not the tick the casualty leaves it. A build that marks its dead and sweeps them once per tick — mark-dead-then-sweep, an ordinary game-loop idiom — scored the bounty, played the cue and stopped drawing the foe on exactly the right tick, but had not compacted its array when the check looked, one tick early. All four reported “the bolt did not kill it” about a kill the recording plainly showed, with the bounty assertion beside them passing:+300,+1000,+200. Nothing inspecs/foes.mdpins when a destroyed foe leaves the array — only that it “dies to a single bolt and pays a bounty when killed” — so the old reading passed the reference for the incidental reason that itshitFoesplices synchronously. A newactShootFoeDeadhelper waits for the outcome instead, bounded to 30 ticks: long enough for any sweep, far too short for a foe posed mid-board to have left by drifting instead. Verified against four builds and a mutant that pays the bounty and never removes the foe — which all four items still catch. - Wireworm —
blocked-by-segmentcurled the worm into a six-tile spiral with its tail parked directly ahead of the head. No sequence of legal steps produces that shape — a worm drops one row per turn and flips its vertical heading only at the top or the floor (specs/worm.md) — and the tile the head dropped into was occupied by the worm’s own body, so the reference came out of the turn with two segments stacked on one tile. The item was reading each build’s answer to a question the case never asks. It now poses a real climb-and-flip path (up to the top row, flip, back down into the row-1 trail it just crossed), with the blocker a mid-body segment rather than the vacating tail and the drop tile clear, and asserts the stacking directly: a build that reads “blocked by another worm segment” as meaning only other worms’ segments walks its head through its own body, and now fails by name. - Meltdown —
sampleTowerBodyread a single interior point, betting the tower body is filled solid; a build drawing a lit frame around a dark interior paints the whole heat ramp on its border and read as flat. It now masks to the pixels that move between the two posed states and summarizes with a median. - Across several cases, checks that dereferenced a missing unit threw, which the
runtime reads as a broken debug API — reporting a script
TypeErroras a contract breach rather than the check’s own verdict. They now report the failed requirement.
Clips that filmed the wrong window
Section titled “Clips that filmed the wrong window”act returns the instant its predicate holds, so a clip cut on the frame before
the payoff; elsewhere the default 8 s budget was spent on the approach and stopped
before the behavior. Reviewers were watching the reference appear to do less than
the build under review.
Items across Meltdown, Spectra, Floe, Arc Foundry, Wireworm, Cascade, Shatter, Coil
and Deepcore now skip the approach unfilmed (via the new skip/skipUntil), open on
the behavior under test, run on for a beat after the event, and carry a budget sized
to the scenario — so the clips got shorter as well as complete. Specific repairs
include three Wireworm stills that filmed the title screen because their scripts
never called enterPlay, Cascade’s you-win clip that spent its budget ~4 s short of
the prompt it exists to show, Arc Foundry’s chain/splash packs and strongest/weakest
pairs that spawned superimposed and so were invisible, and Deepcore’s four-band color
still, now a paced tour clip.
A further pass over Wireworm covers the opposite half of the same problem: not a
clip that runs long, but one with no run-up at all. A worm posed one tile short of the
node it charges turns on the clip’s first frame, and a shot fired from act’s first
tick lands before the recording has one — so fourteen items opened on the event they
exist to show, and a reviewer was asked to judge an aftermath. Each now poses the worm
further back and films it winding in, through a new actWormToColumn helper that walks
it to an exact column: a column rather than a tick count, because the item that fires at
the end of a run-up needs the worm on a known tile, and because arriving on the tick the
worm steps leaves a full tile-step interval for the shot to resolve in. Three further
repairs in the same case:
worm.quickensis about a speed, and its output was a single still of a level-12 worm — which shows a long worm and says nothing about how fast it moves. It is now a clip: two and a half seconds of level 1 at its 0.14 s cadence, then the same stretch of level 12 at ~0.08 s, each level’s entry skipped unfilmed, with the HUD’s level readout saying which is which.glitch-eatsspawned its glitch directly onto the single node it eats, one sim beat before the eat, so the clip read as the glitch being born from the node. It now releases the glitch three rows above a row of critical nodes and reads back the tile it actually crossed — the eat is still decided on a node the real systems chose, and there is no column the glitch’s random darting can reach that misses.dropper-sparse-triggerspent six of its eight filming seconds on the dense-field negative control and stopped before the dropper it exists to show drew in. That dwell is nowskipped — the same six seconds of real simulation, so the control is unchanged — leaving the budget to the arrival.
cursor.clamped-band’s leftward sweep also started three-quarters of the way across the
band rather than at the right edge, so it read as setting off from nowhere in
particular; it now runs the band end to end, over four seconds rather than three so that
widening what the clip shows does not quietly turn into a cursor-speed requirement the
specs do not state. And controls.arrow-down films half a second of the posed start
before the key goes down: the band is 32 px tall and a 430 px/s cursor crosses it in
about 75 ms, so without a beat first the clip opened with the cursor already pinned to
the floor and read as one that never moved.
Baseline media
Section titled “Baseline media”Because a repaired script changes what its arrange poses and what its act films,
the committed reference baseline it is compared against has to be re-captured with
it. Baselines were regenerated case by case as the repairs landed — including one
deliberate catch-up pass over Arc Foundry, where an earlier repair of 54 scripts had
regenerated only 9 baselines, leaving 31 items being compared against media captured
from a script older than the one now deciding their verdict.
The catalog’s committed baseline files went from 1,339 to 1,609, its validation scripts from 931 to 1,046, and its scored review items from 956 to 1,037.
The graded flag never reached the reviewer editor
Section titled “The graded flag never reached the reviewer editor”The reviewer editor fetches checklist items from
GET /test-cases/{slug}/versions/{version} and keys its rating control off each
item’s graded flag — the 💩→💎 grade scale for a game-jam category versus pass/fail
for everything else. ReviewItemOut dropped the flag, so the editor always saw
undefined and rendered pass/fail controls for game jams. The snapshot path
serialized it correctly, which is why the read-only verdict and checklist views were
right and only the editor was wrong.
The graded flag never reached the public site either
Section titled “The graded flag never reached the public site either”The same flag went missing one layer further out. The static gallery is assembled at
build time from the published snapshot, and its assembler re-maps each case’s review
items field by field — a list that never gained graded. Everything the site derives
from it therefore read a game jam as a pass/fail case: each category showed as
worth 1 pt instead of weight × 10, a category the reviewer marked 💎 earned
nothing (it was not a pass), and a fully graded seven-category entry published as
0 / 7 points. The whole-game overall grade vanished with it, because the verdict
view identifies a jam by “any item is graded” — with the flag gone there was no jam to
identify, so the badge that stands in for a jam’s rating never rendered.
The assembler now carries graded through, along with each sub-item’s own
description, weight, and paired reference/proof — which had been dropped the
same way, leaving the public site scoring every point of a categories-grammar case as
1 point regardless of its declared weight, and showing the points without their prose.
A jam’s overall grade had nowhere to show
Section titled “A jam’s overall grade had nowhere to show”Two surfaces asked only for a rating, so a jam — which has no scoring domains and therefore no rating — showed a bare dash. The run detail header now falls back to the reviewer’s whole-game overall grade beside the run title, and the home page’s latest result hero does the same in its Rating stat, both resolving it the way the run tables already did: a local, in-progress review first, then the summary card’s aggregate.
The grade badge itself also drops its emoji. It exists to be a jam’s rating badge, so it now renders exactly like one — a tier-tinted chip carrying the label alone — rather than a wider chip that broke the column wherever it sat beside real ratings. The emoji still belongs to the input scale: the reviewer’s grade buttons and the per-category checklist rows.
The graded scale was harsher than it read
Section titled “The graded scale was harsher than it read”A graded category is worth weight × 10 points and earns its tier’s points. Those
points were 0 / 1 / 3 / 5 / 10, which put 😀 Great at half marks and 😐 Neutral at
30% — so an entry a reviewer thought well of published as a middling score, and a jam’s
percentage could not be read against a test case’s earned-over-declared score at all.
The tiers are now worth 0 / 2 / 5 / 8 / 10: neutral earns half its available points
and great four fifths. Ranking is unaffected (the change is monotone); what changes is
what a given verdict publishes as. The reviewer editor also shows a jam its live
score as it grades, which it previously suppressed — the one review flow that hid the
points it was accruing.
A published run kept offering to publish itself
Section titled “A published run kept offering to publish itself”The review editor is offered on a run the active worker produced or one the signed-in
account has already reviewed — the second clause so a reviewer can correct their own
review from anywhere. But nothing downstream knew whether the run was already public:
RunDetail carried the record and its reviews and dropped the store’s published
flag. So revisiting a published run — which a reviewer does exactly when they want to
revise their review — presented a Publish run button for a run already in the
gallery, which the backend would refuse.
published now travels with the run detail (the console reads the store’s flag; the
static site serves published runs by definition), and the editor retires the Publish
action once the run is public on both the web and solo-desktop paths, leaving the
review controls that still apply — revising a published review refreshes the public
snapshot on its own. A publish that succeeds this session retires the action
immediately rather than waiting for the record to be refetched. This is one lifecycle
for every test type: a game jam is submitted and published exactly the way a test run
is.
A coverage plan’s progress emptied the moment its target rose
Section titled “A coverage plan’s progress emptied the moment its target rose”The plan cards on the account Coverage tab filled their bar with
cellsSatisfied / cellsTotal. A cell is satisfied only once it has met the plan’s
target outright, so that ratio is a step function of the target rather than a measure
of how much of the plan is done — and the one edit a reviewer makes most often is
raising the target, reusing last release’s plan for the next one. A plan covered at
2 runs/cell has every cell satisfied and a full bar; set it to 3 and every cell
leaves satisfied at the same instant, so the bar reads empty on a plan already
holding two thirds of the runs it wants, and stays empty until whole cells land on the
new target.
The bar now measures runs. The summary already carried enough to say so: the target
total is cellsTotal × runsPerCell, and runsMissing is the sum of the per-cell
shortfalls, so the runs already accounted for — completed plus in-flight, capped per
cell — are the difference. The same plan raised to 3 runs/cell reads two thirds full
and advances run by run. The count beside the bar still reports cells covered and runs
missing, and its tooltip now names both, so the bar’s units are never in question. The
per-cell and per-case bars on the plan dashboard were already run-based; this is the
list view catching up to them.
Smaller fixes
Section titled “Smaller fixes”- Cascade’s
follows-cursordemandedwaste.length === 0while a card was held — an internal modeling choice no spec mandates. It now checks detachment in pixels: the source slot must stop painting the card, and the point under the cursor must not look like that slot. - Floe’s
ice/slidesandwater/driftopened a measured span inarrangeand closed it inact; the runtime settles the build onto its manual clock only between those phases, so the span also contained the driver’s own round trips. Both ends are now read inact. - Coil’s one-shot cue triggers moved into
Game.advance()on both variants, so a cue fires identically whether the tick came from a real frame or a scriptedstep(). - Arc Foundry gained a
menuButtons()readback, so menu items take the mouse path the spec makes primary rather than the keys it only offers as an alternative. - Meltdown’s
info.countsnow selects the tower, since the tallies only render in the inspector. - Wireworm’s reference implementation lost two lives to the fatal contact. Each
per-tick contact test is followed by a
phase !== "active"guard, which is what stops one life loss being processed twice in the tick it happens — but only the respawn branch ofloseLifesetsphase; the game-over branch has no respawn to sequence and leaves it"active". So on the last life both guards passed,checkCursorHitran again in the same tick against the worm that game over deliberately leaves standing, andsnapshot()reportedlives: -1. It now refuses to lose a life once the run is over. (Nothing caught this before because the items that end a run posed the worm on top of the cursor, where only one of the two tests ever fired.) - Cascade’s test-case description was shortened.