Correctness Pass: Path Deadlock, Module Caching, Texture Seams, Sprite Facing

Four fixes, found by reviewing why certain problems kept recurring rather than by chasing them individually:

ES modules were being served cached. The game is unbundled modules at stable URLs with no cache busting, so a reload could silently re-run the cached copy of a file just edited. It never presents as a caching problem — it presents as "the fix didn't work," which invites re-fixing or reverting changes that were already correct. Caught directly: a verified edit was on disk while the page demonstrably ran the old function body. tools/dev_server.py now serves everything Cache-Control: no-store; use it instead of python -m http.server.

An unreachable destination froze customers permanently and leaked their table. isPathComplete() required path !== null, so a failed pathfind reported "not complete" forever. Worker tasks survived by accident via a separate guard in BaseTask; the customer state machine had none. A guest sent to a walled-off bathroom never finished eating, so their table was never released or marked dirty — one sealed door quietly removed a table from the restaurant for the rest of the run. Reproduces on the pre-existing LEAVING and WALKING_TO_TABLE states too. Null now counts as complete, the restroom trip refuses to start when the bathroom is unreachable, and a related occupancy-claim leak on the same path was closed. Two regression tests added.

Wall and counter runs looked like rows of separate blocks. Two compounding causes. The fill callback ran once per piece and stretched a fixed texture crop into each piece's rect — but a straight run's arms are ~5x narrower than its hub, so the material was squashed to a fifth of its width on the arms and changed scale twice per cell. And the trim windows were not tileable: measured by tiling each crop and averaging the luminance step across the cell boundary, the counter stepped 35.4 horizontally / 40.4 vertically and the wall 10.9 / 64.6, i.e. a hard seam redrawn at every cell. Pieces are now clipped as one region with the fill mapped once across the whole tile, and both trim windows were re-solved against that measurement: counter now 0.9 / 1.2, wall 3.1 / 2.0.

The sprite sheet is not a real 4-direction set. Two independent traps, both of which read as "the code is drawing it wrong." First, a _n/_s/_e/_w suffix names the side the object's BACK is on, not the side it faces. Second, and worse, four files does not mean four views: measured by mean per-pixel difference, 8 of 15 sprite families ship the same artwork twice on the n/s axis (toilet 15.0 vs 86.5 for the genuine e/w pair; counter 6.6, bathroom_sink 7.2, trash 12.0, pass 13.7, grill2 16.5, fridge2 20.0, table 5.1), and table/small_table/chair2 are duplicated on both axes, making them effectively single-view assets. chair3 is distinct on both axes, which is very probably the real reason the chair art had to be swapped twice before it looked right — that was never a code bug. Where a real crop exists it is now used; where the needed view genuinely doesn't exist in the art, it is synthesized by flipping.

More from CHEF! · See this in the live feed