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.
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, with no wall clock, no filming budget, and 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, because 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, and 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, since 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, which is 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, which are 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. There were 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, because what repeats a game is the model rather than 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, with RunRecord.gameJamPriorEntries carrying 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, rather than 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, so 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”This is 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 had six such 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 had 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 pinned the level-clear and bay awards 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 were sized to the reference’s own emerge delay, where the spec says only “after a short delay”, and a floe-drift check matched floes across a step by array index, which the snapshot contract does not promise.
- Valence required the hard map to have three tracks and never cross (the spec says two or more), assumed the curved map was the single-path one, measured a tower’s aim from a muzzle offset, and read the kinetic bond bonus 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) has gyre-variant obstacles that 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, so a gentler-but-conformant AI failed, because the AI is deliberately slower than the human and eases off near the ball. It now reads spin against the paddle’s actual velocity. - Wireworm 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’s 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.
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, so 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 re-centres when the cursor actually reappears read as one that never did it; the board-clearing half is still read at the touch, and the cursor once the respawn has run. Its ±1 px bound also 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, so 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’s
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. 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 alongsideuserKeyanduserClick, 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”This is 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’s
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 treats an empty posed field as 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’s
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”This is the mirror defect, found by mutation: an item that passed a build with the behavior it exists to verify deleted outright.
- Fathom (Kindle) passed all six of the vision-circle variant’s own checks
on 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’s
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 had seven checks that 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’s 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, clearing the victory cascade before it drew a frame while 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’s 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’s
combine-actions-onlyrequired an action set omittingcomborecipeunder a label expressly allowing it; a newbuild/explicit-set-folds-exactlycovers a claim nothing checked. - Meltdown gained a new
targeting.fire-ratecovering 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’s
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 gained a new
instrumentation/manual-clockitem scoring 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’s
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’s
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’s
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’s 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, 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. It is verified against four builds and a mutant that pays the bounty and never removes the foe, which all four items still catch. - Wireworm’s
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, since 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’s
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, so 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 and the budget goes 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, from 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”, so with the
flag gone there was no jam to identify and 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, because 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, making it 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. Nothing downstream knew whether the run
was already public, though: 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,
and 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, since 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
and 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.