# Verification — 16 September 2026

## Drain-anchored steam

Steam and grey vent plumes now originate at the road artwork's drain grates. The emitter shares road crop metadata, image scale, tile parity and camera speed, including mirrored repeats. Fire barrels retain their existing placement. `steam.test.cjs` compares emitters against the actual road draw transforms across all districts, three viewport widths and six camera positions. Scenery browser, road-join and ES5/artwork checks pass. Tokyo drain alignment was visually reviewed in-game.

## Road repeat boundaries

Road rendering now samples horizontal ranges whose endpoints lie in unpainted gaps: Tokyo 42–1492, New York 56–1480, Shenzhen 44–1494. These exclude partial dashes at the original image edges. Mirrored repetition preserves all four complete markings per tile without merging or truncating paint at a join, retaining source aspect ratio and the pavement baseline. `road-joins.test.cjs` measures actual paint pixels in the renderer-selected crop and verifies three consecutive mirrored repeats. All-district scenery browser, parallax and ES5/artwork checks pass; the Tokyo road was visually reviewed in-game.

## Tokyo and New York pavement roads

Built-in ImageGen redrew both road textures using Shenzhen as the reference: continuous pavement, raised curb, drains and matte textured asphalt with restrained damp patches. Tokyo uses white markings; New York uses worn yellow markings and a manhole. `road-redraws.json` records exact prompts, source paths, crop bounds and byte sizes. `pack-roads.cjs` removes blank framing and proportionally downsamples to 1536 pixels wide, then writes optimized quality-88 JPEGs (4:4:4 colour), totaling 237,158 bytes. The pavement-1 URL revision refreshes cached roads. Road position, parallax and six-pixel facade overlap are preserved. Road pixel/size checks, all-district scenery browser checks at three sizes, parallax and ES5/artwork checks pass. Tokyo and New York in-game screenshots were visually reviewed.

## Narrower gaps between blocks

The current spacing uses solid building columns (alpha at least 200 over at least 15% of a section's height), excluding faint residue and isolated pixels that previously inflated the bounds. Neighboring solid edges are separated by 24 world pixels. This replaces the previous 72-pixel spacing based on any nonzero alpha, which left visibly large gaps. Artwork scale, pavement anchors, full six-section traversal and gameplay length remain unchanged. The actual Tokyo arcade-to-market boundary was visually reviewed after the fix. Pixel-based spacing checks cover all fifteen joins; browser checks cover all districts and three sizes; parallax and ES5/artwork checks pass.

## Solid pavement alignment

The image packer now identifies the last broad, solid pavement row (98% of the widest opaque row in the lower quarter), excluding faint alpha residue below it. Every block is bottom-aligned using that real pavement baseline. This fixes the floating headquarters and other sections without changing aspect ratio, architectural scale or transparent side gaps. The asset revision is open-blocks-2. Pixel tests check all eighteen packed sections reach their final row with solid pavement; browser checks assert the baseline overlaps the road by six pixels across all districts, route positions and section joins at three sizes. The Tokyo and New York headquarters were visually inspected after repacking. Scenery, parallax and ES5/artwork checks pass.

## Scenery section joins — complete building blocks

All eighteen near-background blocks were regenerated with built-in ImageGen: finished building sides, complete roofs and actual transparent space on both ends. Optimized outputs replace the existing facade, extension and sector assets, so district-only loading is unchanged. The renderer draws each entire block at one proportional scale, with no horizontal source crop, alpha blend or overlapping architecture. All sections are 1,280 world units wide; the full six-block route scrolls slower than the road. The skyline is one continuous image across the route, with its top above the viewport; the far layer uses mirrored tiles with matching boundary pixels.

Pixel checks verify transparent 32-pixel outer margins across every row, transparent roof headroom and indexed PNG budgets. Browser checks visit every one of the five block joins in all three districts at three screen sizes, asserting full source width, full opacity, equal horizontal/vertical scale and uninterrupted skyline coverage. Tokyo and New York joins were visually inspected. This replaces and supersedes the earlier cropped/faded seam treatment and per-doorway crop sizing. Tall portrait views show complete roofs against the distant sky rather than stretching or slicing buildings to fill their height.

## Building height and road seam

The street is drawn once at 211 pixels high, starting six pixels above the previous road boundary. This overlaps the facade without repeating the upper road strip. Each district uses one shared doorway scale, at least 110 world pixels, increased for taller viewports so the shortest facade clears the screen top with roof headroom. Equal horizontal and vertical scaling preserves aspect ratio; horizontal cropping fits each section. Scenery browser checks pass for all three districts, five route positions and three viewport sizes. Landscape Tokyo/New York and portrait Shenzhen screenshots were visually reviewed. Parallax ordering and ES5/artwork checks pass. This supersedes the earlier 80-pixel doorway and duplicated seam-strip treatment.


## Default sound startup

The active authoritative viewer attempts sound once it is ready. Saved mute preferences remain respected; suspended contexts keep the Enable sound button and defer audio downloads. A viewer click, touch release or keypress retries suspended audio; the sound button retains its explicit toggle behavior. Resume rejection is handled without an unhandled promise rejection. `autoplay.browser.test.cjs` checks audible automatic playback before and after refresh in Chrome with autoplay allowed, a simulated rejected-autoplay policy through refresh followed by a real click elsewhere on the viewer, and saved mute followed by explicit enable. Ordinary clicks preserve explicit mute. Browser scenarios and ES5/artwork checks pass.

## Rooflines and building scale

Reviewed all eighteen replacement street sections and inspected their source row boundaries for pavement leaking into the next roofline. All panoramas preserve aspect ratio; door measurements produce consistent human-scale buildings across old and added sections. The browser scenery test checks those ratios and doorway sizes in all districts at five route positions and three viewport sizes. Transparent headroom, per-district loading, foreground depth order, ES5 compatibility and unchanged 7,800-unit district progression are covered by the existing regression checks. Source images and final in-game screenshots were visually inspected.

## Extended districts and layered scenery

All districts run 7,800 world units, up from 3,900. Encounter gates double their previous distances, and the boss waits until the final stretch beyond position 7,150. Debug boss starts still jump directly to the arena. Each district has six unique facade sections, an additional skyline, tall foreground lamps/signs, transparent animated fire/smoke/steam and passing background vehicles. The attract loop now uses timed patrol shots.

`scenery.test.cjs` checks district distance, boss trigger location, direct boss starts, indexed PNG size budgets and actual alpha. `scenery.browser.test.cjs` renders five positions across all three districts at 960×540, 450×960 and 1920×1080 and checks animation, foreground rendering and browser errors. District loading tests check that other districts remain unloaded, old assets release and failed downloads remain retryable. Existing engine, attract and ES5/artwork tests cover progression and animation regressions.

## Supplied intro/demo music

Michael Gardiner's user-supplied “Take It Back Tonight” is the intro cue for both attract scenes and all three comic pages. Entering the comic preserves the playing song; the final page crossfades to district music. A new game after defeat restarts the song. The original audio stream is preserved, with embedded artwork removed to reduce the asset from 4,557,312 to 2,441,969 bytes.

`intro-music.browser.test.cjs` passes live stereo decoding and audible output, no download before sound is enabled, continuous demo/comic playback, mute/resume, native looping, one intro fetch across scene changes, district crossfade, release of the previous decoded buffer, and replay. `audio-loading.test.cjs`, `district-music.test.cjs` and `assets.test.cjs` also pass. The nine generated chiptune cues retain their separate mix checks; supplied-song metadata is retained by the score generator.

## Attract loop — patrol, sparring and encounter

The lobby shows robots and drones patrolling right to left for 10 seconds, six fighters sparring for 5 seconds, another 10-second patrol, then 10 seconds of sparring. Robots approach over eight seconds as humans arrange on the left, then hold battle formation for six seconds before repeating. The separate fighter march is removed. Existing 1.2-second fades and 0.35-second black holds surround the shots. Patrol speed remains 90 world units per second. Timing, animation and untouched game-state checks pass, along with live browser tests at three sizes, controller start during a fade, refresh and ES5/artwork checks.

`attract.test.cjs` checks the route, complete walking and combat frame sequences, fade progression, loops, screen bounds, district asset compatibility, and isolation from real game state. `attract.browser.test.cjs` checks actual rendering and screenshots at 960×540, 450×960 and 1920×1080, full-black pixels, real controller joining/starting during a fade, normal story/game startup, and restart on viewer refresh. Existing `browser.test.cjs`, `district-loading.test.cjs` and `assets.test.cjs` pass: controller handoff, reconnects, six-player limits, QR navigation, per-district loading and ES5 compatibility are preserved. No browser errors.

## Controller home screen after viewer refresh

Controllers now open their setup menu when the viewer state changes back to the lobby. The existing menu-opening path also clears held controls. Ordinary lobby updates do not force the menu open repeatedly.

Expanded `viewer-refresh.browser.test.cjs` to finish the intro and connect two controllers before refreshing the viewer. The new visible-menu assertion failed before the fix and now passes. Both phones return to setup with Start and fighter selection enabled, reset health/score/weapons, clear held movement, retain their player identities, and launch a fresh game. No browser errors.

## Mid-game controller join recovery

An accepted viewer offer starts the existing 2.6-second response timeout even before a player session exists. Repeated offers do not extend that deadline. If the join never completes, the controller clears its stale host and discovers the current viewer again, retaining its player key.

`midgame-join.browser.test.cjs` reproduced an indefinite wait before the fix by injecting a departed viewer's offer during a live New York boss fight. It now passes: normal late joining, recovery from the stale offer, movement and attacking, preserved fight/score, no duplicate slots, and refresh recovery with the same character and score. `presence.browser.test.cjs`, `controller-start.browser.test.cjs` and `assets.test.cjs` also pass, including full-crew waiting and replacement of a disconnected player. No browser exceptions.

## Projectile direction

Bolt sprites now face their horizontal velocity: the bright tip leads and the trail follows, including Johnny Five's volleys. Passed `projectile-facing.browser.test.cjs` against the live server: real Johnny Five volleys travel left/right with matching canvas sprite transforms, stay upright, and leave stationary mine orientation unchanged. Both screenshots reviewed; no browser exceptions. `assets.test.cjs` also passes ES5 and asset checks.

## District loading presentation

Replaced the plain status box with a full-screen arcade panel showing district number/name, a segmented progress bar and the percentage of successfully loaded artwork. Failed downloads switch to amber and expose Retry; failures cannot count toward 100%. The panel uses only local HTML/CSS and adds no asset requests.

Passed `district-loading.browser.test.cjs`, `district-loading.test.cjs` and `assets.test.cjs`. Browser testing uses real app files and artwork with simulated RCWeb transport and deliberately delayed/failed image requests. Checked progress, failure, retry, completion, district changes and shared-asset reuse. Screenshots reviewed at 960×540, 450×960 and 700×362; layout assertions also cover 1920×1080. All loader content fits without scrolling. No browser exceptions; production JavaScript remains ES5-compatible.

## Soundtrack revision 4 — individual district and boss themes

Passed `district-music.test.cjs`, `music-balance.test.cjs`, `music-balance.browser.test.cjs`, `music.test.cjs`, `music.browser.test.cjs` and `assets.test.cjs`. All six combat themes have different melodic/rhythmic signatures after pitch normalization. Each district and boss selects its assigned MP3; boss music continues through collapse, then district victory takes over. Browser checks cover all nine decoded files, loop joins, stereo/headroom, pause/resume, mute/restart, title/end cues and bounded decoded memory. No browser errors.

The nine files total 539.581 seconds and 8,647,328 bytes (8.25 MiB). Pitched/drum RMS ratios in the tested eight-bar passages range from 0.232 to 0.274. Revision 4 cache URLs load the new mixes. Playback review is available at `soundtrack.html`; verification covers composition and signal behavior rather than subjective audition.

## Automated behavior

`node src/main/apps/app/beatemup/engine.test.cjs`

**22 behavioral tests passed.** They check six unique hero slots, rejection of a seventh player, input/session validation, reconnect identity and score, stale-input release, pausing, attack startup and single-hit resolution, lane/facing checks, no friendly fire, action-counter latching, guard breaking, weapon durability, jump anticipation/landing, dodging floor attacks, ally and automatic revival, all ten enemy strategies, mine arming, moving projectiles, floor-before-explosion timing, and snapshot restoration. Presence tests cover expiry without simulation frames in every phase, fixed reconnect deadlines, session validation, slot reuse and connected-player start/revive counts.

The campaign test walks through all twelve sector exits using the actual camera and movement constraints, with enemies cleared by the test fixture. It verifies route progression and both district transitions; it is not a claim that a human play-tester completed and balanced every encounter. Camera tests also verify that a trailing player stays visible and a leading player cannot run off the right edge of a portrait display.

## Asset and compatibility checks

`node src/main/apps/app/beatemup/assets.test.cjs`

- All production `.js` files parse with an ES5 grammar.
- The app styles avoid the checked modern-only layout features.
- All **816** combat/source and motion rectangles are within their image bounds.
- Every character has 36 combat/source poses. Each humanoid has eight complete ImageGen walk drawings and six intact guard drawings; each robot dog retains its sixteen walk and six guard poses.
- All sixteen opposite walking contacts differ. The animation behavior test checks distance-driven frame selection, wraparound, facing and the separate guard clock.
- All **196 humanoid walking/guard frames** have connected silhouettes, with no detached opaque component larger than 25 pixels. All **84 guard silhouettes** retain greater than 99% overlap with their original full-body drawings. These checks detect broken packing and detached pieces; visual inspection is still needed to judge the stride.
- The four robot-dog combat and motion images have unchanged SHA-256 hashes.
- Every character has a short, horizontal floor pose.
- Character and motion sheets are indexed PNGs.

## Real RCWeb browser flow

The following multiplayer checks passed again with the illustrated joystick controller, controller Start and presence fixes, and current artwork and soundtrack.

`node src/main/apps/app/beatemup/browser.test.cjs`

Passed in headless Chrome against the running RCWeb server, using separate browser contexts/pages:

1. Click the actual Beatemup tile in `/v-c/`. The `/v/` viewer opens Beatemup and existing `c` and `v-c` controllers follow into `beatemup-c` in the same room.
2. Press **Start the uprising** on a phone with another controller waiting in `/v-c/`; the late controller follows and existing controller sessions survive.
3. Join six distinct fighters. A seventh controller waits visibly.
4. Send simultaneous touch movement and held punch through Chrome's touch input. The authoritative player moves, punch repeats, release clears movement, and a jump lifts the player.
5. Normalize and save a player name in `rcwebName`; reload retains the fighter's key, slot and score.
6. Check landscape and portrait viewer layouts, the standard QR target and controller layouts.
7. Click **Back to apps**. Only that controller navigates; a waiting controller can claim its released slot.
8. Open a second viewer, disconnect the authority, and verify takeover plus controller reconnection with the retained score. Advance the score on the new authority, reconnect the original viewer, and verify it adopts that newer state instead of replacing it with its older checkpoint. Close the original viewer and confirm the new authority remains active.

The final multiplayer browser run reported **zero page errors**.

## Controller Start regression

`controller-start.browser.test.cjs` passed. A joined non-captain phone can launch from the lobby and restart after defeat or victory. A keyboard captain no longer disables phone restart. Invalid sessions cannot start, active play cannot be restarted, pause/resume remains captain-only, and controller sessions survive launch. The full multiplayer test also launches using the phone button instead of the viewer's Start button. Both browser tests reported zero page errors; ES5 and asset checks passed.

## Controller presence regression

`presence.browser.test.cjs` passed against the running server. Closing the only lobby controller drops the connected count to zero, disables Start and releases the slot. A brief reload preserves identity and score. Network loss is detected while the game is paused, transfers captain status and allows a quick reconnect. Hiding a controller stops its heartbeats and discovery; expiry releases its slot and returning rejoins cleanly. Back to apps waits for removal acknowledgement before navigating. A seventh controller automatically takes an expired slot.

The silent-failure fixture leaves the controller's WebSocket open but suppresses its app messages and stops viewer animation callbacks. The player is marked disconnected after three seconds and removed five seconds later without advancing the simulation clock. The browser run reported zero page errors.

## Illustrated joystick controller

`../beatemup-c/controls.browser.test.cjs` passed with zero page errors, exercising the real viewer and controller with Chrome touch input. It checks the joystick's centre dead zone, all eight directions, bounded thumb travel, dragging outside the pad, spring-back and independent multi-touch: holding punch while dragging, then lifting the joystick finger while punch continues. It also checks touch cancellation, mouse dragging, keyboard input, loss of focus, rotation release, every illustrated action and the charge meter. The final portrait and landscape captures were visually reviewed.

The layout checks cover 320×568, 360×640, 390×844, 450×960, 568×320, 667×375, 844×390 and 960×450. All control targets stay within the viewport, are at least 44 pixels across and keep aligned action rows and columns. The five action illustrations were generated with built-in ImageGen and reduced to 160×160, 64-colour indexed PNGs with no dithering and maximum compression: 74,421 bytes combined. Prompts, original sources and production filenames are recorded in `../beatemup-c/controller-art-sources.json`.

## Full-body animation browser regression

`node src/main/apps/app/beatemup/animation.browser.test.cjs`

Passed after installing the ImageGen walks:

- Start a local player in the real viewer, walk, release movement and exercise punch/jump input without page errors.
- Confirm the viewer and animation review page load the same 68 images, including 28 versioned humanoid combat/motion images and the combat-effects atlas.
- Capture renderer calls and verify that every pose uses exactly one complete sprite draw, with all eight walking rectangles selected at their frame boundaries.
- Exercise quarter speed, actor selection, pause and scrubbing; capture walking and guard contact sheets for all sixteen actors.

This caught floating-point rounding at exact frame boundaries, which could repeat a preview frame. The corrected frame selection passed the rerun. No limb assembly or joint deformation remains in the renderer.

## Combat effects replacement

`combat-effects.test.cjs` and `combat-effects.browser.test.cjs` passed. Checks cover no impact on a miss or during wind-up, one impact at contact, distinct kick and guard effects, weapon trails surviving the final weapon use and snapshot, invulnerability rejecting duplicate hits, pausing and expiry, mine explosions, all 36 isolated transparent frames, all six weapon-trail source rectangles, facing and effect lifetime. Robot destruction starts after the floor pose rather than on initial knockdown. Live game captures of weapon impact, robot explosion and lean-mode overdrive were visually reviewed. Browser checks reported zero page errors. The animation browser regression, 22 engine checks and ES5/asset checks passed too. The new atlas adds 62,307 compressed bytes and 2.25 MiB decoded RGBA.

## Visual review

Reviewed viewer compositions at **1920×1080, 960×540, 1080×1920 and 450×960**, in lobby and gameplay. Reviewed phone controls at **450×960 and 960×450**, and the character menu. Invitation panels stay within the viewport and preserve the standard QR frame, host header, room footer and same-room controller link.

Reviewed generated combat contact sheets for all six humans and all ten robots: guard, punch, kick, airborne attack, hurt and floor. The latest visual pass reviewed full-body ImageGen walking and guard contact sheets for all fourteen humanoids, the two retained dog cycles, and the real viewer. Sheets with faulty foot order, incomplete feet or inconsistent facing were regenerated. The animation lab displays the same complete sprites as the game.

Reviewed both city layers at four camera positions, including storefront joins. Removed repeated reference storefronts from the continuation panels and cropped transparent padding below the pavement. Checked the rendered app information pages and their images. Missing automatic 512-pixel icon references were fixed; the final visual/resource pass reported no failed resources or page errors.

The temporary visual review captures and helpers are under `tmp/beatemup/`. The app information screenshots are stored with each app. Some review captures use staged actor positions or a paused simulation to make comparisons repeatable.

## Original soundtrack replacement

`music.test.cjs` and `music.browser.test.cjs` passed for the six original rendered tracks. The score contains 5 minutes 26 seconds of music, including two 64-bar city arrangements, and the compressed files total 4.99 MiB.

The browser decoded every production MP3 at 32 kHz, confirming encoder padding is trimmed to within 0.01 seconds, stereo content is present, peaks remain below clipping and loop joins are clean. Live tests covered title, both cities, boss, victory and defeat; pause/resume position; a native loop crossing; the visibility handler and authority gate; finite victory playback; mute through reload and restart; and release of the outgoing decoded buffer. The soundtrack review plays only one track at a time.

The final focused check also forced a music request failure, verified the visible error, retried successfully and confirmed that the error cleared. Simultaneous combat effects and music retained headroom. The review page was checked at desktop and 450-pixel phone widths, and the updated app information rendered correctly. These tests reported zero page errors. The 16 engine tests and ES5/asset checks also passed after integration.

Independent ffmpeg measurements put the compressed tracks at −16.6 to −15.6 LUFS integrated and −5.7 to −3.2 dBFS true peak. Audio input was unavailable in this session, so the mixes were rendered and measured but not auditioned by the assistant. The exact game mixes are available on [the listening page](soundtrack.html); composition and production details are in [SOUNDTRACK.md](SOUNDTRACK.md).

## Armed walking and weapon use

- Generated and visually reviewed the eighteen armed eight-frame walking sequences against the accepted full-body walks. Corrected hand switching with ImageGen; retained opposite leading legs, attached hips and passing poses.
- `armed-assets.test.cjs`: 648 packed full-body poses, 144 distinct walking frames, indexed PNGs, valid rectangles, connected figures and wide floor poses. Isolating each connected cel fixed a neighbouring figure leaking into an extended sword's crop.
- `armed-animation.test.cjs`: the armed and unarmed walks share the same distance phase; stopping resumes the previous stride; the last weapon use retains its swing; kicks, airborne and floor states select armed poses.
- `armed-animation.browser.test.cjs`: all eighteen actual weapon pickups, eight-frame playback in both facings, armed guard/jump/floor/final-use swing, no floating item overlay, 68 loaded images and zero page errors. Contact sheets are saved in `tmp/beatemup/armed-walk-0.png`, `armed-walk-1.png` and `armed-walk-2.png`.
- Existing 22 engine checks, ES5/asset checks, viewer/review parity and combat-effect browser checks passed after integration. Networking, controller and audio behavior were not changed in this correction.

## Performance scope

The production viewer fetches **71 locally served images** and uses complete sprite-frame playback, packed rectangles and a bounded canvas resolution. The production images total **13.29 MiB** compressed and approximately **169.3 MiB** decoded RGBA. The lean path removes rain, shake and expensive CSS effects and targets 30 draws per second. It was exercised in the local renderer.

`render-benchmark.json` records a desktop Chrome **draw-command submission** microbenchmark with six humans and thirteen robots. It excludes completion of deferred GPU work and is not an end-to-end FPS measurement. The image totals separately estimate decoded RGBA storage; browser overhead is additional.

**Not physically verified:** six actual phones on Wi-Fi, Safari/iOS, or a Samsung Tizen 2 display. ES5 parsing and the lean renderer are useful compatibility checks, but they do not establish performance on those devices. Timing and difficulty may still benefit from a real group play session.

## Scope

The new app pair, its generated artwork, research and tests are included. Catalogue entries were added in the three required app lists. No backend, Maven or server behavior change was needed. Existing unrelated workspace edits were preserved.
# Drawn fall verification

`fall-animation.test.cjs` checks 144 distinct packed poses, all 24 horizontal floor holds, palette/alpha/bounds, six ordered animation stages, zero player rotation, pause, initial recovery and floor contact before game over. `fall-animation.browser.test.cjs` exercises 384 actual viewer draws across every hero, equipment state and facing. It throws if the player renderer calls canvas rotation, verifies six different rectangles followed by a held last frame, locks the impact direction, and ensures stale attack effects do not render. All 68 images load with zero page errors.

Four proof sheets (`tmp/beatemup/drawn-falls--1.png` through `drawn-falls-2.png`) were visually reviewed at game scale for connected bodies, articulated knees/hips, ground registration and weapon grip. The engine, armed animation, viewer/lab parity and ES5/asset regressions also passed.
# Encounter boundary verification

`blocked.test.cjs` verifies the player reaches the right screen margin at 640, 960 and 1280 game widths, while the encounter limits camera scrolling. It checks retreat, warning persistence and pause/snapshot behavior, immediate dismissal after the last robot, and progression to the next encounter. All 22 engine checks still pass, including twelve encounters across both cities and keeping the trailing player visible. `blocked.browser.test.cjs` exercises the real renderer at 960×540 and 450×960 with zero page errors. Both screenshots were visually reviewed for right-edge access, legible warning text and placement above the fighting area.
# Opening comic verification

`backstory.browser.test.cjs` checks the 2030 caption, three panels in order, frozen combat, portrait and landscape layout, artwork clicks and controller next-page actions. Pages remain until explicitly advanced; the final page alone starts combat. Replay returns to page one, Enter advances once, and Escape cannot skip the story. Existing combat fixtures click through all three pages before exercising gameplay.
# Robot spacing

## Unique district bosses

`district-bosses.test.cjs` verifies warning before impact, hit timing at the drawn contact, eight distance-driven locomotion frames, idle/attack frames and the final wreck hold. Packing checks 32 distinct nonidentical cells in each optimized alpha/palette sheet. `progression.test.cjs` verifies the dedicated Gundam/Johnny Five assignments, health scaling for one/six players, snapshot persistence and boss-gated level completion. `progression.browser.test.cjs` draws all 32 cells in both facings for both bosses with zero canvas rotations, and checks actual giant sprites at 960x540/450x960. Source sheets and game screenshots were visually reviewed. All 22 engine tests and ES5/assets tests pass; viewer asset count is now 71.

## Smartphone viewer controls

Narrow portrait touch phones (up to 600 CSS pixels wide) show a local joystick and five illustrated action buttons in place of the crew list; the QR invitation and Join button are hidden. Fine-pointer desktop windows retain the normal viewer layout even on touch-capable computers. `phone-controls.browser.test.cjs` verifies local launch, all three story pages, two-thumb movement and held attacks, all five action counters, pause release, control bounds at 320x568/390x844/450x960, rotation preserving the player, and desktop QR/crew visibility. Phone screenshots at 320 and 450 pixels were visually reviewed. Controller launch and ES5/assets tests also pass.

## Soundtrack revision 3

All six melodies were replaced with chord-matched phrases; chord voicings and root/fifth bass support were corrected. Pitched instrument event gains are 28% of revision 2 and pitch wobble was removed. `music-balance.test.cjs` verifies every percussion event is unchanged and every pitched note belongs to the current chord. `music-balance.browser.test.cjs` measures pitched/drum RMS ratios of 0.274, 0.271 and 0.234 in representative Tokyo/New York/boss passages. All six MP3s were re-rendered, loop joins smoothed, and revision-3 URLs added. Score/master checks, browser decode/playback tests and ES5 checks pass. These checks measure composition and signal properties; they are not a listening review.

## DJI-inspired aerial enemies

Three drone types (indices 10–12) use one ImageGen sheet of 24 drawn frames, including banked dives and crashed wrecks. `drones.test.cjs` covers hover immunity to ground punches, jump reach, telegraph-before-damage, low recovery vulnerability, armed bomber charges, crash descent and attack opportunities for all three drones. `drones.browser.test.cjs` checks all 24 source cells, zero sprite rotation, actual sheet loading, and rendering at 960x540 and 450x960 without page errors. Both screenshots were visually reviewed. Engine, progression, robot-spacing and ES5/assets tests pass. Existing browser asset-count fixtures now expect 69 images.

The transparent sheet is 768x768, 128-colour indexed PNG, 147,281 bytes. Full prompt and source provenance are in `drone-source.json`. Drone progression is introduced at encounters 2/3/4; opening scouts and final solo bosses remain unchanged.

## Boss positioning and expanded attacks

All three bosses seek `camera + viewport width × 0.6` between attacks. Boss knockback is reduced and clamped inside 120-pixel screen margins; returning movement continues during hit reactions and accelerates when far from the preferred position. Damage and player combo rewards still apply, but ordinary hits do not cancel boss warnings/attacks indefinitely. Charge movement is also bounded by the arena edges.

Gundam cycles ground slams, two travelling ground shockwaves and three-lane shot volleys. Johnny Five cycles track charges, three marked mines with a 1.1-second arming delay, and shot volleys. Sentinel adds paired tentacle warnings every third attack when available, then alternates core pulses and volleys after its limbs are defeated. Warnings name the move; effects reuse existing district-loaded ImageGen art. Boss death removes its outstanding projectiles.

`boss-combat.test.cjs` passes for knockback/recentring at widths 640/960/1280, shifted cameras, six-player kick pressure, all attack rotations, damage without repeated interruption, projectile dodging/jumping, mine arming, paired targets, core volleys, death cleanup and snapshot sync. `boss-combat.browser.test.cjs` passes real viewer starts at all three bosses, checks bounds and return to 60%, exercises every move and captures warnings/impacts in landscape and portrait. Screenshots were reviewed with no browser exceptions. Existing boss animation, Sentinel combat/attachment, 22 engine and ES5/assets checks also pass. The Sentinel dodge fixture now supplies the fixed warning target used by real gameplay.

## Sentinel socket and directional animation fix

The previous renderer placed each collar directly above its claw, leaving roots outside the body and a vertical gap. The replacement uses eight sockets inside the opaque core artwork and full drawn poses anchored at both collar and claw. A new 16-frame ImageGen sheet supplies sideways bends, coiling, strikes and recovery; the eight drawn severing poses are retained in a single 176,161-byte indexed PNG. Tips fan in multiple directions, warning targets stay fixed, and visible height controls attack reach.

`sentinel-attachment.test.cjs`, `sentinel.test.cjs`, `sentinel-attachment.browser.test.cjs` and the updated Shenzhen sprite/browser tests pass. Coverage includes socket overlap in every live core frame, connected/gutter-safe artwork, actual rendered endpoint positions in both display orientations, warnings/dodging/counterattacks, beating all eight limbs with normal combat and snapshot synchronization. Updated idle/strike/recovery/severed screenshots were reviewed. The 22 engine checks, ES5/assets checks and district-only loading checks also pass. Full prompt/source and packing notes are in `SHENZHEN.md` and `sentinel-motion-source.json`.

## District asset loading

The viewer loads the current district's assets, selected from the engine's encounter pools. Shared fighter/weapon/effect sheets are retained; previous district-only images are released. The animation lab deliberately retains its all-assets mode. Tokyo loads 64 instead of 79 game images (24.8% fewer image bytes), New York 66 (22.5% fewer), and Shenzhen 57 (29.7% fewer). These figures exclude the additional savings from deferring story/completion illustrations.

`district-loading.test.cjs` checks complete encounter coverage, deferred authority selection, shared-image reuse, stale load callbacks, previous-district release and retrying only failed assets. `audio-loading.test.cjs` verifies one current music request, cancellation of obsolete downloads/decodes, release of old decoded buffers, and suspension while graphics load or sound is muted. The shared combat-effects sound bank remains on demand when sound is enabled.

`district-loading.browser.test.cjs` verifies actual image/audio requests with simulated RCWeb transport, including a delayed background, frozen combat, local presence through a delay longer than three seconds, failed-image retry, replay, deferred ending art and silent mirrors loading only the host district. `district-loading.live.browser.test.cjs` passes against the real local server: phone Start and Continue, mirror synchronization through New York to Shenzhen, correct asset requests, shared reuse and previous-district release, with no browser exceptions. Existing engine, ES5/assets, district-music, all six query-start browser checks and normal backstory browser checks also pass. The server became available again, allowing the previously blocked query-start live tests to pass.

## Query-selected test starts

`level-start.test.cjs` passes for all six district/boss selections at logical widths 640, 960 and 1280 with one and six players. It checks URL parsing and invalid/duplicate rejection, immediate boss/crew/pickup visibility, eight intact Sentinel tentacles, fresh replay state, continuation through remaining districts, and snapshot preservation. Existing 22 engine behavior tests and ES5 parsing of both changed scripts also pass.

`level-start.offline.browser.test.cjs` passes using the actual viewer scripts and artwork with a stubbed RCWeb connection. It checks all six starts, local buttons, authenticated controller command dispatch, replay, matching music, boss visibility in portrait, invalid URL errors, and the ordinary three-page intro when no level is supplied. No browser exceptions occurred.

Live runs of `level-start.browser.test.cjs` and `backstory.browser.test.cjs` could not reach page load: the existing RCWeb process was listening on port 8080 but HTTP requests timed out, including a direct check outside the sandbox. Live controller networking and mirror synchronization therefore remain unverified for this change. No backend code or server process was changed.

## Level progression

`progression.test.cjs` verifies both cities with one and six players: strictly increasing average robot size and health across six encounters, small scout opening, all ten robot types represented, one large final boss, scaled melee/projectile damage, snapshot preservation and no level completion while the boss lives. Existing 22 engine tests, robot spacing and ES5/asset tests pass. `progression.browser.test.cjs` verifies both boss sprites render above 200 logical pixels and the LEVEL BOSS objective at 960x540 and 450x960. Boss display screenshots were reviewed; the health banner is positioned clear of the encounter panel.

Robots spawn in spaced rows, share nearby human targets, and approach from separate flanks. Two robots per target may commit to attacks at once; waiting robots occupy three lanes and gain priority as their cooldown runs down. Local separation contributes to actual walking distance, while committed telegraphs and attacks keep their original movement.

`node src/main/apps/app/beatemup/robot-spacing.test.cjs` verifies ten initially overlapping robot types spread out, all get attack turns, the two-attack limit holds at both lane edges, nearby players share pressure, missing players are retargeted, and snapshot restoration remains deterministic. `robot-spacing.browser.test.cjs` checks actual rendering at 960x540 and 450x960, with no browser errors. Both screenshots were visually reviewed. Existing 22 engine behavior checks and asset/ES5 checks passed.
