Danillo Felixdaal · How I built it · Alpha v0.2.0

The road to
Redline Wars.

I wanted to bring the feeling of our after-work RTS matches in Amsterdam into a game anyone could open. Here is how I built it with a laptop and an army of AI agents.

Updated 7 October 2026 · Currently free to play

How I built Redline Wars

I built Redline Wars in my spare time with a laptop, an army of AI agents, and a stubborn memory of the strategy games I loved growing up. This is the route I took, including the turns that did not work.

I am Danillo Felixdaal, an Amsterdam-based developer who spent years building web and mobile products before turning much of my attention to AI architecture. I co-founded ProofOfWork, now becoming Twenty+One, where I work as a Build Lead: one human responsible for the decisions and the result while specialist agents do much of the making. Redline Wars is mine, built after hours under Jolyn Studios. It gave me a personal place to test the same question I face at work: how much can a small team do when the human keeps direction and the agents have real tools, shared context, and proof that their work holds up?

I have a habit of building an idea far enough to let it argue back. In our concept lab, ideas are sparred, prototyped and given a verdict, including the ones that fail. In other projects I have explored visual AI workflows and ways for agents to coordinate. This game was the less sensible and more personal experiment: what if that way of working could build the strategy game I still wanted to play?

The game I wanted to play again

I grew up with Command & Conquer, Red Alert, and Yuri's Revenge. I remember the music as clearly as the units. Later, after work in Amsterdam, I played Generals with friends in internet cafés. We sat at separate computers, ate beside the keyboards, shouted warnings across the room, and stayed for one more match. A win came from playing better. That feeling was the reason I decided to build this game.

I wanted a modern 3D battlefield that still felt like those evenings: a clear set of rules, a battle you can read, and no purchase between a player and the outcome. The browser mattered because I wanted someone to open a link and play. The desktop app came later, when I needed a practical way for players to host their own matches. Both open the same game.

I kept asking what Red Alert felt like rather than how to copy its pixels. For me it was readable armies, industrial machines, factions you could recognise at a glance, dramatic light, and music that told you a battle was coming. I wanted the 3D world to look like the battlefield I imagined while staring at those old maps. That gave the agents an art direction to work toward, not permission to reuse somebody else's models or recordings.

Redline Wars is currently free to play, made with respect for Westwood Studios and Electronic Arts. I used OpenRA's open-source engine and made new art and sound for Redline Wars. No EA or Westwood assets or recordings ship with it. The playable Alpha offers skirmish and multiplayer; the Fractured Order story gives those battles their setting, rather than promising a finished campaign.

One developer, many hands

My laptop was the development machine: an Apple M3 Pro MacBook on which I built, played, measured frames, and watched things fail. It was not a server that players depend on. I made the product decisions and judged the result; AI agents helped me carry out the work.

The people, harnesses, and evidence loop Danillo · human directortaste, scope, final call ContextRelay · agent bridgehandoff · debate · shared context Claude Code · Opus 4.8 / 5architecture and repo-wide reviewasks: does the design hold? Codex CLI · GPT-5.6focused code and second opinionasks: does the patch work? SpecialistsFable · Astra · GLM · Grokport · art · systems · review One repository and buildpatches meet the same contracts Evidence gatesreplay · pixels · network · release validate

I built a private coordination layer called ContextRelay for our work at Twenty+One and connected Claude Code and the Codex CLI through it. They could hand off a bounded task, inspect the same repository, review one another's work, and return a disagreement to me with evidence. One agent could be busy implementing while the other checked the architecture. When they disagreed, I wanted the argument and its evidence, not a quiet compromise in two separate chats. The bridge made a small team possible, but it did not make decisions for me. I chose the next problem and what counted as finished.

JobModel or platformWhy it belonged here
Architecture and broad reviewClaude Opus 4.8, later Opus 5, in Claude CodeKeep the engine, art, network, and release rules coherent.
Focused code and second opinionCodex on GPT-5.6 in the Codex CLIImplement bounded changes and challenge another agent's assumptions.
OpenRA fork and browser portFable 5.1, under my directionFable implemented most of the WebAssembly port while I guided the work and made the product decisions.
Models, animation, and repairAstra 6 in Blender, then GLM 5.3 Flash for the wider asset pipelineBuild authored assets and make their export repeatable.
Multiplayer systemsGLM 5.3 FlashRun long, broad implementation tasks against the service plan and network gates.
Independent reviewsGrok 4.6Challenge code and asset decisions before they became expensive to change.
Multiplayer and measured finishGrok 4.7 alongside GLM 5.3 FlashHelp bring the network work across the line, then chase large-battle frame cost and live defects.
In-game voice and videoFreehop, our own open-source SDKThe calling layer had to exist without per-minute bills or streams through my relay.
Film, music, voice, model detailHiggsfield AI, Suno 5.5, ElevenLabs, MeshyGive the game characters, a score, faction and character voices, effects, and a consistent in-game likeness.

The crucial skill was matching the harness and model to a bounded job, then asking another agent or a gate to challenge the result. A confident answer and a clean compile were only a start. The code, replay, pixels, and live page had to agree. With those checks, I could move between a rig, a sound bank, a shader, and a server without hiring a department for each one.

The first hard choice: keep one truth

I chose OpenRA because its mature, deterministic simulation already knew how to run a real RTS. My rule was simple: OpenRA decides the battle. The 3D client shows it. A renderer must never quietly move a unit, decide a hit, or invent an explosion. That choice shaped everything from the browser port to multiplayer.

Getting OpenRA into a browser was the hardest single piece of engineering. A desktop engine assumes sockets and APIs a browser cannot give it. I worked through a WebSocket transport, a browser-driven simulation pump, and a trimmed WebAssembly build. Then I built a determinism check: run the same seeded battle in desktop .NET and browser WebAssembly and compare their state and sync hashes. Until those results matched, a beautiful browser battle would have been an imitation, not the same game.

Fable 5.1 and I opened the openra-web fork from the upstream OpenRA tree. I guided the port and set the boundaries; Fable did most of the WebAssembly implementation. The fork let us keep the engine's battle rules while replacing desktop assumptions at the edges. The work gave OpenRA a browser platform and connection factory, an in-memory local connection, and cooperative steps for work that had expected its own thread. JavaScript supplied browser storage, input, audio, graphics and WebSocket interop; the mod rules and required files went into the browser filesystem, with persistent settings in IndexedDB. The result was a trimmed .NET 8 AOT build. OpenRA still advanced at 25 simulation ticks a second; the screen could draw at its own rate.

The TCP decision was more specific than “TCP does not work on the web.” A browser WebSocket itself runs over TCP, but a normal page cannot open OpenRA's arbitrary raw TCP socket. The first bridge translated browser WebSocket traffic to the original server's TCP connection. Later, the room node and public relay made that path usable for player-hosted matches without asking a player to expose the server port. The browser port changed the transport and host plumbing, not the combat rules.

The determinism oracle mattered more than a screenshot. It replayed one seeded battle in desktop .NET and browser WebAssembly, compared the state over time, and caught drift before it could become a multiplayer desync. Codex and GLM later helped build and maintain that proof. It gave me a precise answer to the question I kept asking the agents: is this still the same game?

The boundary became a compact message each simulation tick. OpenRA emits a packed snapshot; TypeScript reads it and feeds the image, sound, and interface. Player orders travel back to OpenRA for validation. This is why the browser, desktop app, and dedicated host can share the same engine rather than growing separate versions of the rules.

OpenRA engineWebAssembly · 25 Hz lockstepWeb Worker → snapshotthe only truth Snapshot decoderTypeScript, zero-copy viewsevent republisherpollSnapshot() WebGPU renderer3D, read-only Audiovoice + SFX banks HUDminimap · roster · orders orders, validated

The snapshot is packed binary rather than JSON because it can describe hundreds of actors without repeatedly spelling out field names. Its sections carry identifiers, lengths, and offsets. The TypeScript decoder reads views of one buffer, and unknown event kinds can be skipped. I made a strict rule at this seam: the simulation advances on its own schedule; the renderer consumes the latest snapshot and may smooth motion between ticks, but it does not tick the engine or decide any gameplay. When I later moved the simulation into a Web Worker, that boundary let the interface remain responsive while pathfinding and world generation ran elsewhere.

I wrote the renderer directly in WebGPU because it receives a packed stream of actors, not a scene graph it owns. That gave me control over batching, shadows, lighting, and the frame budget. The site's single-model showroom uses three.js; a model on a pedestal is a different problem from an RTS battle.

In the renderer, that decision meant custom WGSL shaders, instanced draws, clustered lighting for many simultaneous flashes and searchlights, cascaded shadows, temporal smoothing, and tone mapping. Those are names for the work the graphics card performs to turn a battle state into a picture. The business choice underneath is simpler: I owned the visual pipeline so I could spend detail where the player sees it and prove how much a frame costs. The price was that every shader bug was ours to find.

Giving the picture character

The first playable battlefield made the architecture visible, but every early unit and building was generated from code: cylinders, boxes, and later procedural hulls meshed from shapes. Even the battlefield sound began as waveforms and filters from our own synthesizer. That let me prove movement, orders, terrain and combat before an art pipeline existed, yet from the normal camera height the armies had too little identity. I changed course and brought authored Blender models into the build. Astra 6 laid roughly 95 percent of the 3D foundation. GLM 5.3 Flash finished the export and integration pipeline so the models could be rebuilt rather than copied by hand. Grok 4.6 and Codex challenged asset decisions in review. The picture changed from a technology demonstration into a place where I wanted to command an army.

This was where “inspired by Red Alert” became a set of visual decisions: strong silhouettes before tiny details, faction colour visible at battle distance, machinery that looks heavy enough to move, weather and a day-night cycle that change mood without hiding orders, and damage you can read without opening a panel. The industrial material set added paint, metal, rust, and grime. I compared results from the game's normal camera height, because a beautiful close-up of a tank is worthless if ten tanks look identical when you are commanding them.

I had to break my own procedural-only rule. The original shapes were cheap to generate, but they could not tell a player what kind of tank or building they were looking at from a normal camera distance. I allowed authored Blender geometry and carefully sourced CC0 or CC-BY material instead. The files became a small factory of their own: more than 650 Blender primary sources, materials with wear and grime, model-detail levels for distance, and damage states that move a building from a scorch to a ruin. Every imported source needed a licence and a pinned origin. Every export had to agree with the game's scale and actor list.

The MCV, the mobile construction vehicle, was the showpiece and the hardest art handoff. It had to read first as a vehicle and then as a construction yard in one continuous motion. Astra built the deploying rig with 256 bones and 246 animation channels, aligning the moving parts rather than hiding the transformation in a model swap. There were five authored damage stages too, so the result still had to make sense when the building was hit. GLM helped turn Blender sources, rig data, animation, materials, and damage meshes into repeatable game packs. Hash and visual checks guarded the trip from source file to match. That is a lot of work for one object; it is also the moment the synthetic-looking battlefield began to feel built.

From generated draft to a game-ready cast Astra 6 · Blendersource meshes, rigs, materialsMCV: 256 bones, 246 channelsauthored foundation GLM 5.3 · export pipelinehash-checked model packsLOD, wear, five damage stagesrepeatable from source WebGPU battlefieldthe roster players commandreviewed at RTS camera scalepixels, not just files Meshy · Riki detailcloser likeness to filmdraft came back deformed Astra · measured repairrig, rifle, weights, poses223 frames checked Higgsfield AI · four filmscommand, Kirkov, Riki, Jacksongenerate and reject Human art-direction reviewfaces, uniforms, shared warregenerate until it belongs One recognisable castfilm and playable roster agreethe same Redline Wars

Generated work still needed a ruler. Meshy helped develop Riki's in-game model so the commando in the roster could resemble the woman in the film. One export came back with a deformed rig and a rifle that bent like rubber. My attempts to fix it by eye made it worse. Astra repaired the constraints, shoulder, hair and sleeve weights, UVs, and rifle grip, then checked 223 sampled frames and 147 action switches. The repair stayed only when the moving model held together. That episode taught me that an AI-generated asset is a draft until the animation and export survive measurement.

The films were another production loop. Higgsfield AI gave me a camera, characters, and a way to stage briefings without a film crew. I directed Allied command, Kirkov, Riki, and Jackson as four scenes from the same war, cut to the game's own score. Most generations did not survive comparison with the game's costumes, faces, and visual language. I rejected and regenerated them until the film characters belonged beside their in-game models. Riki needed the Meshy and Astra detour precisely because the film had made that mismatch obvious.

Allied command · the briefing
Kirkov · the red line
Riki · behind enemy lines
Jackson · make it count

The faces were not invented at random. Jerome is based on Jermaine, my cousin and long-time development and business partner. Kirkov carries the likeness of my friend Sebastiaan. Riki is named for my wife Rikie, and Jackson carries the likeness of Anthony, a long-time friend with a Dutch military background. They agreed to be part of this world. That made the films more personal to me, and it made consistency between the screen and the playable roster matter even more.

Making room for a second player

Once the same battle could run in a browser, the next question was who would host it. I did not want every match to depend on a game server I owned. The desktop app became a thin Electron shell around the very same AppBundle the browser opens. It gives a player a landing screen and can start the bundled hosting node when they choose to host. The browser remains a permanent way to play, and its join path remains in the code behind a runtime switch. The public browser switch is now set to join: browser players can join hosted rooms; hosting remains in the desktop app.

Claude and Codex helped me argue through the architecture before I let the wide build begin. The choices had to work together: who owns a room, what the relay may know, whether a volunteer server is a different product, and whether a browser player would still have a path later. We settled on player hosting first. My infrastructure is a meeting point and, if needed, a last source of capacity. It is not the machine every match must run on. The relay may know that a room exists, but it must not interpret an order or decide who won. A dedicated server comes from the same OpenRA engine build as the clients; it runs headless as an order authority, while the clients simulate the battle. The desktop app must wrap the browser game, not grow another game inside it. Those decisions became written boundaries the agents could implement and test.

GLM 5.3 Flash was the workhorse for the broad multiplayer build: room nodes, relay, client flow, packaging, and the network gates. Grok 4.7 worked with that pass on review and the problems that appeared when the pieces met. Grok 4.6 had been an earlier independent reviewer. I could afford to let GLM work across a wide service plan because the decisions were pinned, Codex and Claude could inspect the seams through ContextRelay, and the gates could reject a room that only looked connected.

I built the hosting path around players' machines. A host's node dials out to the public spine relay, so a player does not have to open an inbound router port. The relay lists rooms and forwards match bytes without interpreting the battle. A dedicated OpenRA server from the same engine handles each room, forwards orders and watches for sync disagreement; the clients run the simulation and draw it. The community node package uses the same source, manifest, assembler, and start logic as the node in the desktop app. A player's app starts one room on demand; a volunteer can install the package and keep standing rooms available. There is one game build and one node build, with different packaging around them.

How a match is reachedBrowserthe play sitethe same game bundlepublic join enabled; no hostingOne node sourcenpm package and the zipsthe desktop starts that sourceone assembler, every systempackaging never rebuildsDesktop appElectron shell, own landingsame bundle underneaththe node ships insideNode in the appone match, on demanddials out to spine /nodeLAN can skip the relayDedicated serverone OpenRA.Server per roomclients simulate the battleorders and sync hashesJoining playerjoins at play.redlinewars.online/g/ROOM through the relayhost address stays hiddenRelayplay.redlinewars.online: roomsspine.redlinewars.online/nodeplayers in, nodes dial outblind byte pumpCommunity nodethe same package, as a zipstanding roomsdials out, same as the appsame node assemblystartsrelayjoinspawnsdials out

Here is one internet match in plain language. I choose Host in the desktop app. It starts the node already packaged with the app. The node starts one dedicated OpenRA room and opens an outbound encrypted connection to wss://spine.redlinewars.online/node. That is the node's entrance to the spine. The public room directory and player-facing socket live at play.redlinewars.online: a second player receives a room URL shaped like wss://play.redlinewars.online/g/<room-id>. The relay gives that socket a channel on the node's existing connection. Its control messages say which room and channel; its binary frames carry the players' game bytes. The node forwards them to the room's loopback-only server. Both clients run the same simulation and exchange orders; the dedicated server detects sync disagreement. Neither player needs the other's home address. On a local network, the desktop apps can discover the room and talk directly to the host's node, skipping the public relay. The public browser can play skirmish or join a hosted internet room through that same player-facing route.

The node and relay have distinct jobs. The node manages rooms and starts the dedicated process. A community installation can run several standing rooms from the same package. The relay supplies the room directory and a tunnel to nodes that dialed out. It may reject an incompatible build, cap traffic, or disconnect a broken socket; it does not parse the battle. The dedicated engine forwards orders and checks the tick at which two clients disagree. That separation makes a volunteer node useful without turning the central service into a second simulation.

Open player-hosted matches and trusted Ranked results are deliberately different problems. A player-hosted room is unranked: neither the host nor a browser can send the account service a victory claim and have it accepted. For Ranked, the separate path I built and verified locally uses an owner-controlled room to record the order stream. An isolated verifier replays those orders with the matching OpenRA simulation, reads the actual end state, and signs a result or a void reason before the account service can settle it once. It reuses the game rules instead of inventing a second referee. That was a separate qualification step before the public rollout.

This architecture was tested by awkward failures, not delivered by a perfect first plan. Two browsers could lockstep while keeping separate shroud state. Lobby buttons once waited on a tick nobody called. A long model-processing task blocked a join past its time window, so that work had to yield. An older network contract lingered after the client had moved to /v2, and I removed the legacy paths rather than maintain two meanings for a room. A browser-map experiment relied on filesystem enumeration WebAssembly could not provide; it broke boot, so I reverted it. The later Web Worker and asynchronous bridge kept the page responsive while the engine ran. The desktop shell stayed glue; it never gained its own copy of the game.

There are limits I chose deliberately. Ranked keeps its human-opponent requirements. An unranked room can start with one seated human and AI opponents when the map and readiness checks pass. The table has at most five player seats; observers do not take one, and optional empty seats do not block Start. Rooms that are still booting stay out of the public list. Public browser joining remains controlled by the off / join / full runtime switch, now set to join.

I tested that promise without opening the public switch. Two browsers first joined one room through a relay running on my M3 Pro, started a match, and kept the shared frame counter moving without a desync. Another test put two nodes behind the relay, routed players through their tunnels, and reached matching frames without an out-of-sync flag; a separate check confirmed that each player kept a different view of the unexplored map. After the public page could boot, I repeated the shape through the owner's gated browser route: one browser hosted, the directory listed the room, and the other joined. Both stayed connected while the sync check remained clean. These were local and gated network proofs, not a claim that two consumer machines on different networks had completed a public release match. The public browser setting remained off during those early tests; the later release opened joining.

Finding the sound of this war

The engine and network could now run a battle, but the first sounds still felt like a prototype. I remembered how the music in Red Alert changed the mood before the first tank moved. With Suno 5.5 I searched for a theme with that drive, judged the results against the game's own identity, and chose a 142-second piece that could loop from the boot screen into a match. I compressed the selected track to about 1.7 MB so it would not dominate the first download. It evokes the games I loved without shipping their recordings.

That first theme grew into a twelve-track score. Cold Start now loops through loading, setup and the lobby. Once the battle begins, ten tracks shuffle without repeating the one just played; Hard-Won Victory and Quiet Burial belong to the result screen. Reverse Order, Still Standing, Mechanical Groove, Mechanized Expansion and Dusk Over The Field give the battle different moods. Solo Mission is Riki's theme; I chose Beyond The Enemy Lines for the trailer and We Hold The Line for the capture scene. All twelve recordings are in the shared game bundle and on the soundtrack page.

I kept playback under the player's control. Sound and volume settings persist, the browser waits for a gesture when autoplay is blocked, and music stays off while the microphone is live so the score does not spill into the call. On 7 October I checked the actual served game: every soundtrack file was present and matched the website's recording, including the complete Reverse Order track.

Sound made the same trip as the models. At first the guns, engines and impacts came from the game's own procedural audio engine. The earliest announcer tests included locally rendered text-to-speech, followed by recorded faction banks and character voices. As the library grew, I moved every voice bank to ElevenLabs: faction announcers, characters such as Riki, Spy and Jackson, and death shouts. One provider now voices the whole battlefield, and each bank keeps its own country's timbre. I wrote lines by faction and situation, rendered them before the game was built, and listened for pronunciation, character, and timing. The Allied bank speaks English; other banks speak French, German, Russian, and Ukrainian. I transcribed generated clips back to text and compared them with the intended words, because a file named “construction complete” can still say the wrong thing. One later fix made building completions say exactly that while units still say “unit ready.”

ElevenLabs sound generation then gave the effects banks distinct weapons, impacts, movement, and battlefield cues. An earlier inventory counted hundreds of banked voice clips and 190 rendered effects across five faction banks; the working banks later grew beyond that checkpoint. The recorded clips take priority, and the procedural engine still covers a missing effect instead of leaving a battle mute. A shout gets a voice budget so an army cannot queue a wall of speech, and sounds hidden by fog stay inaudible as well as invisible. I baked the chosen audio into the shared game bundle at build time. A match never calls a voice or music provider over the network. The creative platforms helped make the sound; the finished game owns its playback.

How the battlefield found its voiceFirst playableprocedural effectslocal speech testsfast, but syntheticRecorded voicesElevenLabs factionsElevenLabs characterspronunciation checkedBattlefield soundElevenLabs effects12-track scorechosen, then compressedThe game bundleclips baked at buildno live provider callssynth fills gapsprocedural effects remain as fallback

The assistant beside the commander

In those internet cafés the loudest player was sometimes the one without a keyboard. Someone watched the screen over a shoulder, called out movement, and argued about the next target. That memory shaped the last big feature I built: JOA, the in-game assistant. JOA is the voice beside you in every battle. It greets you, reports what the factories are producing, warns when the base is under attack, and calls every support weapon as it fires. The companion is one part of JOA. It pairs a phone or tablet to a live match and gives the person on the couch their own screen: the tactical map, command groups, support weapons, and a statistics sheet. The game itself answers a finger too: a tap selects, two fingers pan and pinch, and the lobby fits a tablet.

The phone must never become a second god-view. I set the boundaries before any code was written. The commander stays in control of every order. The companion sees only what the commander's own vision of the field reveals, so a paired phone leaks nothing through the fog. Permission is granted in tiers: spectator, technician, or commander. Pairing needs a one-time code and the commander's approval, the host of a match can refuse companions entirely, and the link can be ended at any moment.

The architecture follows the one rule that shaped everything else in this project: OpenRA decides the battle, and every screen shows it. The game already emits its packed snapshot each tick, so the companion renders from a projection derived from that snapshot, filtered to what the local player may know. Orders travel the other way as intents over a WebSocket, and the main game checks every one: the link session, the sequence window, the permission tier, the map bounds, the group revision, aircraft availability, and whether an attack target is actually visible. Only an intent that passes becomes the same order the commander's own interface would issue. If the check fails, nothing happens, and the phone is told why.

What a paired screen may know Match snapshotthe engine's own recordpacked each tickthe only truth Phone or tabletmap · groups · alerts · statsa player-safe projectionthe fog rules still hold Every intent checkedsession · tier · boundsgroup revision · visibilitynothing is trusted twice The same order paththe commander's own Nothing happensthe phone is told why filteredintentpassfailjoins the commander's orders

The sound followed the banks, not a new voice. The new lines, production reports, the base-under-attack warning, and the support calls, were rendered with ElevenLabs into the same per-country voices the shout and death rows already use, each bank speaking its own language. Nothing is synthesised during a match; a line either exists as a banked clip or stays silent. The companion has its own acknowledgment and alert sounds, and a switch to turn them off.

Claude and Codex built it under my direction, trading implementation and review through ContextRelay while I made the calls on scope and safety. The hard discussions were not about drawing a map on a phone. They were about which ten facts a second screen may know, what happens when a group is disbanded mid-order, and how a rejected order should be explained on a small display. The statistics sheet shows credits, ore, power, harvesters, the enemy count and the roster because the game tracks those. Where the engine does not track a number, the sheet says so instead of inventing one.

The companion is now enabled in the public game's configuration. It opens from a pairing link or QR code on a phone or tablet, with approval and permissions still controlled by the commander. That is separate from Freehop: pairing a second screen does not join a voice call, and joining a call does not grant game controls. Ranked rooms keep companions disabled. The couch gets its observer while the commander's screen stays in command; checking the experience across physical phones and networks is still part of Alpha testing.

The call across the room

The cafés had one more sound I wanted back. Friends shouted warnings across the room, argued about the next target, laughed at a failed rush. Text chat in a lobby is useful, but it is not the same room. I wanted voices and faces inside the match itself: join a call from the room, watch the roster light up when someone speaks, mute the one friend who narrates every harvester. Nothing between the players and the game; no external meeting app, no invite link.

The browser already knows how to place a call; WebRTC is a mature stack. The problem is what happens when the direct connection fails. Two strict home routers or a campus network that blocks UDP may need a relay, and the relayed traffic becomes a running cost. I wanted the room service to introduce players without also paying to carry every conversation. The game relay lists matches and forwards game traffic; the media takes its own path.

So we built the calling layer as its own product: Freehop, our open-source voice and video SDK for browsers and desktop apps, Apache-2.0, with its own documentation and a live demo that places a real call between two browser tabs. It splits the work across two planes. A small gate introduces the participants and carries sealed envelopes between them, each one encrypted with HKDF-derived AES-256-GCM keys and bound to the room, the sender and the recipient, so a gate cannot read, forge or re-route what it carries. Gates are bounded to kilobytes so media cannot fit through them, and being kicked from a room rotates its secrets. The media plane stays with the session itself.

When the networks block the direct path, the call climbs a ladder of machines that already belong to it: a desktop participant's own gateway, which asks its router for a port through PCP, NAT-PMP or UPnP; the match host's node; another member's gateway; and at the end of the ladder, one participant bridging the others. If no route exists inside the session, Freehop reports unreachable instead of quietly routing the call through a paid relay. Once peers are linked, the call even keeps running when every gate is down, because the signalling moves onto their own data channels.

How a call crosses the hard networks Ticketsthe game's own relaykick rotates the keys Gatesealed envelopes · KBcannot read or forge themnever carries media Once linkedthe peers hold the callgates can go dark Browser participantjoins with a ticketno install, no setup Match host nodethe session's own gatewaybelongs to the call Desktop participantown gateway, router portPCP · NAT-PMP · UPnP No route inside the session?reported unreachable, no paid relay sealed envelopes, kilobytesdirect when the networks allowmediamedia

Freehop was tested the way this whole project tests things. I built a home lab of Linux kernel NAT profiles, symmetric NATs, UDP-blocking firewalls, IPv6-only paths and UPnP routers, and drove real Chromium, Firefox and WebKit engines through them with fake cameras and microphones: 40 of 40 network scenarios placed their calls, including the pairs the early research had proven impossible to connect directly. The gate traffic for a whole scenario stayed between 30 and 210 KB. coturn's own test client exchanged 800 messages over UDP and another 800 over TCP against Freehop's TURN gateway, none lost. Those are simulated-network results from my lab, not field evidence; the SDK is alpha software and says so on its own page.

Redline Wars consumes Freehop through its public SDK. The deployed version is 0.1.0-alpha.3. The game's relay issues admission tickets, a separate Freehop gate handles signalling and STUN, and the session's participants and host supply the media routes. Alpha v0.2.0 includes the call interface, speaking indicators, per-player mute and a room policy that tells a joiner whether audio, video or both are allowed.

On 7 October I enabled the public Freehop service for the existing game and downloads. I then found a smaller switch still closed: Community #1 had no explicit call setting, which safely defaulted to off. I changed that room to allow voice and video after checking it was empty and draining it cleanly. Its public listing now advertises both, and the template for new community rooms carries the same choice. Each player still chooses whether to join and whether to use a microphone or camera; allowing calls never opens those devices automatically.

The earlier browser-to-desktop direct-route test and four physical-Mac frame-time profiles remain useful evidence. They do not complete physical acceptance across Windows hardware, phones and different home networks. Freehop is available as an opt-in Alpha feature while that work continues.

Making it open source was not charity; it was the same rule as the engine seam. The problem is not game-specific, and a calling layer only a game could use would be a worse calling layer. Forcing the SDK to stand on its own made both sides honest: Freehop survives without the game's assumptions, and the game integrates without private hooks. It grew from the protocol research in this repository into its own project with its own evidence file and its own limits page, built under the codename Peerlane; the envelope salts still answer to that name, and the calls they seal answer to nothing but the session they belong to.

The gate that says no

Agents made it possible to change several parts of the project quickly. They also made it easy for one part to assume that another had changed with it. The packed snapshot is the clearest example. If C# adds a field and TypeScript reads the old width, a battle can look fine for a moment and then desync. I gave that boundary a release gate that calculates the layout independently, compares producer and decoder, and deliberately feeds it bad lengths and bytes. A mismatch stops the release.

The Snapshot ABI gate:

C# emitteractors · players · productionfixed field widthsemit Packed snapshotheader + section tableoffsets and lengthsfour-byte-aligned payloaddecode Independent calculatorcounts × field widths TypeScript decoderzero-copy typed views Falsifiercorrupt bytes · lengths · tail Compare every claimheader · physical offsetsdeclared lengths · viewsdrift is immediate Release decisiongreen: compose + publishred: block both state, not proserecount, don't trust

This simplified exchange shows how I used ContextRelay and the gate when a field changed. The transcript illustrates the review pattern; the measured bytes come from the check, not from an agent's confidence:

ContextRelay · private agent-to-agent bridge

Codex

I added a presentation field to the actor snapshot.

GatePASS

The emitted actor section measures 632 bytes.

Claude

The decoder still reads the expected offsets.

GateRED

One test expects 640 bytes; the physical section is 632.

Codex

I corrected the expectation and ran the malformed-input checks again.

GatePASS

The bad length is rejected.

Human

The change can land when the contract checks are green.

A compact map of the other release gates:

Provenance gatessource · licence · exportpinned hashes and model bytesno mystery assets Simulation gatesruntime · deterministic replaymultiplayer · desyncsame seed, same battle Experience gatesvisual · audio · landingframe budget, banks, public buildmeasured, not felt Redstop the release train Evidencecounts, hashes, pixels, logs Human refereemerge only when green recordreview

The larger gate inventory gave me a less flattering truth. A full historical pass ran 201 commands and found 54 failures. Some were product defects; others were fixtures and source pins that no longer described the game. I spent days reconciling both. We fixed boot fixtures, refreshed asset hashes, caught missing engine API members, rebuilt audio coverage, and made deployment checks fail when their evidence was stale. The result was not a dramatic feature. It was permission to believe the next green build.

That work also changed how I used agents. I could ask GLM to build a wide system, have Claude and Codex argue about its boundary, ask Grok to review the risky seam, and still let an independent replay or pixel gate refuse all of us. One agent's output became another agent's input, but the repository and the checks were the common record. A gate nobody runs is only a reassuring file.

A battle that fits the machine

The new models made the game feel right and made each frame more expensive. There were two different bills to pay: how much model data lived in GPU memory, and how much work each displayed frame required. A smaller download, a smaller GPU footprint, and a faster frame are three different measurements. I kept them separate.

Two different bills: memory and frame time OFFLINE MODEL · GEOMETRY ALLOCATIONS, MiB Old full-float layout743.9 First packed layout437.5 · 41.2% less Current packed layout393.7 Three LODs are counted in every bar · textures add 94.0 MiB CHROME ON APPLE M3 PRO · EUROPE MAP · ABOUT 1,135 ACTORS 60 fps frame budget16.7 ms Dynamic / High typical8.3 ms Frame interval measured on one machine; no FPS gain is attributed to packing alone.

The memory numbers come from an offline audit of the assets the renderer uploads, including their three model-detail levels. It is a model of GPU allocations, not a reading from a graphics card. The earlier all-float vertex layout modeled 743.9 MiB of geometry. Grok 4.7 did the quantization work as part of our push to keep the battle at 60 fps: packing normals, tangents, and texture coordinates took modeled geometry to 437.5 MiB in the first measured pass, a 41.2 percent reduction. The current 28-byte static and 36-byte animated layout models 393.7 MiB for the same asset inventory. Textures add another 94.0 MiB in that audit. The stored asset pack is about 151.9 MiB; it is compressed on disk and expands when used. None of these memory numbers alone proves an FPS gain.

LOD means level of detail. A nearby tank needs its full shape; a distant one occupies fewer pixels and can use fewer triangles. The audit counts three levels, roughly full, half, and quarter detail. I kept the silhouettes and authored motion where players can see them, and used simpler meshes farther away. The renderer adds a sticky band around switching distances so a unit does not flicker between levels as the camera moves. That costs a little bookkeeping and, in an earlier pass, some extra draw calls; it buys a steadier picture.

The optimization had more than one round, with Claude Opus 5 and Grok 4.7 helping to interrogate the expensive paths. Early on, shadow passes were drawing tens of millions of triangles and the meadow layer alone spent 13.8 milliseconds of CPU on updates. Splitting shadow detail from camera detail cut roughly a quarter of the shadow triangles. Sticky LOD selection stopped visible popping even though it cost 82 more draw calls in that test. Moving meadow updates onto a cadence brought that CPU cost to 2.75 milliseconds. Those are measurements from that earlier tuning pass; I do not mix its old frame rate with the later Europe battle as if they were one before-and-after test.

I measured frame time separately on my M3 Pro laptop in Chrome, using the Europe skirmish map with roughly 1,135 actors. Dynamic and High, the two settings I timed after the frame cuts, showed a typical frame of about 8.3 milliseconds at full internal resolution. My 60-frame target was 16.7 milliseconds. The laptop display refreshes near 120 Hz, so a typical 8.3 millisecond interval can also mean the picture is waiting for the screen. This is a result for that machine and battle, not a before-and-after speedup claim for the quantization pass or a promise for every graphics mode.

The actual frame cuts were selective. Shadow maps became smaller and their reach shorter; the sun and contact-shadow shaders sampled less; water skipped expensive land lighting; animated soldiers stopped appearing in every shadow cascade. Scenery scanning and the simulation moved off the page's main thread so they stopped delaying mouse input and the picture. I kept full resolution, contact shading, four shadow cascades, and the actors in the measured Dynamic and High battle. I spent less work on shadows and distant detail that were hard to see during play.

ChoiceWhat became cheaperWhat I gave up or kept
Pack vertex data and use smaller index types where they fitLess geometry uploaded and held in the GPUMore careful encoding and shader decoding; the cutout and shadow pixel gates had to stay equal.
Use three LODs with a sticky switching bandFewer distant triangles without flickeringSome extra draw calls and selection bookkeeping; nearby silhouettes stay detailed.
Shorten and shrink shadow workFewer shadow pixels, samples, and repeated soldier drawsLess distant shadow precision; contact shading and four cascades remained in the measured modes.
Move simulation and scenery scans into workersFewer long stalls on the page's main threadA more careful asynchronous bridge; the engine still owns every game rule.
Let Dynamic shed extras when neededMore room inside a slow frameNearby grass and wind go first, before core actors or rules change.

The quality governor is the final trade: on a strong machine Dynamic may add nearby grass and wind after several quiet measurement windows. If frames get expensive, it drops those extras first, then can lower internal resolution or other effects. High starts from the full picture and may shed cost; Ultra+ keeps its richer look and asks more of the machine. I also tried to correct the hard square where natural ground types blended. Roads stayed sharp because a wandering road edge would look broken; sand could look like sand. That first correction was not the last word on those squares; the next chapter tells how they actually went away.

My laptop was a measuring instrument I could inspect, not a universal benchmark. GPU timestamps on that machine returned zeros at first, so I used time between displayed frames and kept the test map, actor count, browser, and graphics mode attached to the result. Later, once the renderer asked the adapter for its timestamp feature, individual passes could be timed; that is how the restored soldier depth pass in the next chapter got its 1.7 milliseconds. I left the other graphics modes without a new performance claim until I could clock them separately.

What a faster frame cost

The day after those frame cuts I sat down to play and saw what the frame clock had not measured. The shadow under a parked truck vibrated. Walking soldiers had holes in their legs. Light on buildings looked flat, distant trees shimmered, damaged trees carried health bars, and wherever a mountain met open ground the terrain broke into square plates. The cuts had been written for my 120 Hz screen: the code comments behind them said "missed 120 Hz" almost everywhere. My target was 60 frames per second, which leaves twice the time per frame, and the cuts had been applied to every graphics mode, Ultra included.

So the second pass measured the picture, not only the clock. Codex ran the experiments and Claude coordinated them: a fixed camera, a fixed sun, the simulation paused, 24 frames, and the variation of every pixel from frame to frame. Each suspect was switched off, on, and off again, so a change that came back when it was restored was a cause and not a coincidence.

How a shimmer is foundA shimmer is reporteda parked truck, a tree lineseen by a playerFreeze the scenefixed camera and sunbattle pausedMeasure the picture24 frames, every pixelvariation, not frame timeOne suspect at a timeoff, on, off againa cause comes backFix, keep the qualitybudgeted per modeno global cutsA gate remembersfails on a regressionbefore any release

The biggest amplifier had been there all along. A sharpening pass at strength 0.28 enlarged every leftover wobble of the anti-aliasing. Lowering it to 0.10 and letting still pixels keep a longer history cut the shimmer on a building from 1.82 to 0.73 on that measure. Screen-space contact shadows were the vibration under the truck: they read a depth image that shifts slightly every frame. Turning them off halved the worst shimmer beside a vehicle and made a building edge ten times calmer, while the full picture changed by 0.13 of 255 at normal zoom. The shadow maps, their filtering, and the baked ambient occlusion stayed, so units still stand on the ground.

The soldiers had two faults stacked on top of each other. The speed pass had taken them out of the depth pass, so anti-aliasing reused the old background through moving legs; putting them back costs 1.7 milliseconds with 217 actors on screen. On snowy maps a second effect remained: the weather layer painted snow on every surface facing the sky, including the tops of their trousers, which on white ground read as holes. Animated units no longer hold snow; a test with the same rifleman in clear and snowy weather went from 35.8 percent pale uniform pixels to 0.2 percent.

The square plates came from a limit in the terrain itself. Every map cell could show at most three materials, so where rock, snow, a road, and a snow variant met, one of them stopped dead at a cell edge. Now every pixel mixes the four cells around it with a gently wandering boundary; roads and concrete keep straight edges without the staircase. The first version of that change pushed a heavy battle from 17.6 to 25 milliseconds at the slowest five percent, so it was rewritten to sample only the materials that actually carry weight.

The humbling result: the build from the morning before the cuts measured 1.90 on the same shimmer scale, no calmer than the cut build. The flicker had always been there; the cuts made it visible. The new build is calmer than both, and Ultra and Ultra+ have their pre-cut shadows back.

LessonWhat I do now
A speed number hides what it costsEvery frame-time claim sits beside a picture-stability measurement.
Global cuts punish the modes that should shineQuality is budgeted per graphics mode; Ultra never pays for Dynamic's headroom.
One setting can outweigh a whole quality tableToggle one suspect at a time, off-on-off, before touching the next.
Per-cell decisions show up as squaresThe terrain decides per pixel; a cell only names its material.
An effect meant for one object lands on all of themWeather, shading, and health bars are checked per kind of object, with a gate.

From my laptop to a player's screen

For a while the site and services built automatically while the game bundle still came from my development Mac. That was a fragile way to ship. I moved the release through one shared build lane: build the browser game once, verify it, then let the desktop installers wrap that exact AppBundle. Desktop packaging refuses a missing or stale bundle instead of silently building another game.

One shared build lane Code and authored assetsone repository: game, site, shellBlender sources and banksauthored once One game buildthe browser AppBundlebuilt once, verifiedpackaging never rebuilds Tests and Mac visual gatestests first, then WebGPU gatesa Mac witnesses the picturered blocks the release Browser play sitethe verified bundle swaps inlast good stays on a miss Desktop installerswrap the exact AppBundlea stale bundle is refused passpass

The Mac visual gates exist because a cloud Linux software GPU failed when the tests read pixels back. Fresh release runners exposed other ordinary problems: a .NET pin, a missing mod library, Blender setup, and shell scripts that assumed a newer macOS than the runner had. Each fix made the factory less dependent on what happened to be installed on my laptop. A separate deploy can update the site, account service, relay, node, game, or downloads, but the browser game still comes from the single verified AppBundle.

A push can run tests; publishing is now an explicit choice from the consolidated release branch. The workflow checks that all approved work is included and that the selected commit is still the current release source. A full release waits for the shared AppBundle, visual gates, desktop packages and Mac node packaging before changing production services. Frame-time qualification happens on the physical Mac, and the bundle that ships must match the measured content digest. The community node zip comes from the same node assembly used by the app. I can still update the website or an individual service through its own guarded route.

How a push becomes a siteChoose a releasecurrent consolidated sourceexplicit dispatchSource and testssource, browser, shell, nodea red run publishes nothingDeployone component or alla running deploy stays putone release at a timeWebsite, accountsthe site you are readingaccounts and public pagesfix the door, leave the houseRelay, node, rankedrelay when the payload changesnode when the package changesranked pins a proven buildbrowser joining stays onOne shared gameone AppBundlea Mac witnesses the pictureplay swaps in one movea missed witness keeps the oldDesktop installers and node zipssame bundle, one node assemblypackaging never rebuilds the gamefull release waits for packages

The last lesson arrived after a build passed. The public page could still fail to open because the server did not allow one script the game imported. Earlier, a folder mirror had deleted a worker script that really was part of the bundle. In both cases the file existed in the engineering story and disappeared in the player's story. I fixed the serving rule in the service that owned it, then checked the actual page. “Built” was not the same as “playable.”

The safeguards now follow the files further. A browser release switches to a versioned AppBundle and preserves the operator's Freehop setting. Download checks compare package hashes, build identities and source references. Website checks cover the version, privacy and credits pages, all twelve soundtrack recordings, and the model catalogue's delivery format. The account and node installers retain recovery checkpoints. A full service rollout still needs an idle or drained room: an ordinary node restart can end a match, so a green build is not permission to interrupt players.

There is also a source boundary I have to keep explicit. The public source edition includes the OpenRA engine changes for that edition, the WebAssembly host, WebGPU renderer, procedural stand-ins and a minimal HUD. It excludes the production HUD, JOA, Freehop integration and production creative assets. Freehop has its own Apache-2.0 repository. A reduced edition does not supply the corresponding source of the complete distributed game. The production release gate currently blocks a new game publication until that correspondence is resolved and verified; a website update or a working live call does not clear it.

Other defects waited until the game met its real surroundings. Civilian houses spilled outside the lots they replaced, so I fitted only those meshes rather than shrinking every actor. Damaged infantry needed smoke that followed the figure. A phone ran out of room loading the full authored roster and kept restarting near the same point; narrow screens now take a lighter silhouette path. The shared rule was the same in each case: inspect what a player actually receives, fix the owner of that behavior, and keep browser play working throughout the rollout. Browser joining is now enabled; hosting remains a desktop feature.

The identity finally had to feel as deliberate as the battlefield. Isa Ozler designed the Redline Wars game logo. I carried the cut shape behind his wordmark into the site's buttons; their lower front piece now slides back and returns like a loading mechanism when you hover. It was a small finishing choice, but it made the site and the game feel like one product. Thank you, Isa.

The numbers behind the story

These numbers come from different kinds of evidence. An asset count is an inventory, GPU memory below is an offline allocation model, and frame time is a Chrome measurement on one M3 Pro laptop. I keep their conditions beside them so one impressive number does not borrow credibility from another. Some inventory totals describe the audited build at that point in the road; they are not a claim that every local copy or later export has the same file count.

What I counted or measuredResult and scope
First playable stretchAbout six weeks of work, including two fully dedicated weeks and evenings around the day job; shipping continued afterward.
Simulation and pictureOpenRA at 25 ticks per second in WebAssembly; the WebGPU renderer can use more than 200 dynamic lights, with a 24-minute day-night cycle. The industrial material set has 29 layers.
Art sources652 Blender primary sources in the structural audit; the earlier workstation inventory counted 3,327 .blend files when library copies were included. The audit found 1,862,511 triangles in those source files before runtime selection.
Resolved game catalog310 audited actors, of which 283 were renderable and 95 buildable. These categories overlap.
Native models and movement277 delivered native models, 1,503 moving joints, and 1,368,266 triangles at full detail in the model delivery. The wider LOD0 tally was 1,481,162 triangles.
Damage59 current damage ladders in the model delivery; the MCV deploy alone uses 256 bones and 246 animation channels.
Character filmsFour Higgsfield scenes: Allied command, Kirkov, Riki, and Jackson. Riki's repaired rig was checked on 223 frames and 147 action switches.
Voices35 planned lines across each of eight faction banks, plus Riki and Spy personas: 328 banked clips in the audio inventory, including 76 death lines.
Effects and musicAn earlier checkpoint counted 190 effects across five faction banks, or 38 per bank; the working banks later expanded. The current score has twelve recordings: ten battle tracks, including the menu's Cold Start, plus separate victory and defeat songs. All twelve live game files matched the soundtrack page on 7 October.
In-game callingFreehop 0.1.0-alpha.3, Apache-2.0, enabled publicly on 7 October. Community #1 allows opt-in voice and video. The earlier 40-of-40 kernel-NAT matrix and coturn interop are simulated-network lab evidence; physical acceptance across supported devices and networks remains pending.
Geometry memory modelEarlier 743.9 MiB; first packed layout 437.5 MiB (41.2% less); current local layout 393.7 MiB. With 94.0 MiB of textures, the respective modeled totals are 837.9, 531.5, and 487.7 MiB. Three LOD levels and smaller 16-bit indices where possible are counted. These are modeled GPU allocations, not live VRAM readings.
Large-battle frame timeOn the M3 Pro in Chrome, Europe with about 1,135 actors: Dynamic and High each showed about 8.3 ms for a typical frame at full internal scale. The 60 fps budget is 16.7 ms. Other modes were not retimed in that test.
Picture pass, 23 SeptemberOn a 218–221 actor skirmish with the Mac also running other heavy work: Dynamic p50 16.4 ms and p95 17.9 ms, 60 fps with 1024-pixel shadow maps and contact shading off; Ultra and Ultra+ back on 2048-pixel maps with 9-sample filtering.
Picture stabilityFrame-to-frame variation on a building at a fixed camera: 1.90 before the cuts, 1.82 after them, 0.73 after the picture pass (High). Worst-case Ultra shimmer beside a vehicle: 4.67 with contact shadows, 1.46 without.
Browser startA cold local start reaches the lobby in 21–25 s and a playable match in about 36 s; building 374 model variants takes 14.5 s of that.
Earlier frame passClassic held about 48–52 fps before the later cuts. The meadow update cost changed from 13.8 ms to 2.75 ms in that earlier pass. These are not directly comparable with the later Europe run.
Release checksA historical full inventory ran 201 commands and initially found 54 failures. A later audit listed 199 live commands and five owner-disabled gates; those are separate checkpoints, not one subtraction. One acceptance pass ran 48 executable gates and 67 map starts.
First downloadAn earlier desktop-path first visit was about 152 MB, with near-zero repeat transfer when the content-addressed cache was warm. The asset pack audit separately counted 151.9 MiB stored. The lighter phone path and its cache policy differ, so the repeat number is not a universal mobile claim.

Where the road stands

Redline Wars is the game I set out to make from those Amsterdam evenings: one rules engine, an authored 3D battlefield, and a browser skirmish you can open directly, currently free to play. The desktop app gives players the route to host or join their own matches. Accounts and the live leaderboard are open; the outcome screen explains whether a match counted. The public browser setting is join, so browser players can join hosted rooms. JOA's companion is enabled, the twelve-track score is installed, and Freehop is available in rooms that allow it. The current public game version is shown at the top of this story. The public source edition has a reduced feature set and does not reproduce this full distributed game.

That September release was about the room around the battle: opening as an observer without occupying a seat, leaving the previous lobby before hosting another, removing human players or AI, keeping the chosen capacity through map changes, and letting a promoted admin operate the room. Readiness comes back from the engine. An idle admin yields after ten minutes when another human is waiting, with exceptions for a lone admin or a match already running. Lobby text chat is temporary: at most 100 visible messages, no history for a new arrival, no saved chat or replay chat, and a clear on departure, disconnect or Start. Returning from multiplayer to skirmish was tested in the packaged Mac app.

The release also taught me to test the factory itself. An earlier rejected deployment briefly left nodes on a different simulation build from the relay. I rolled it back, checked the restored services, and fixed the dependency gates before publishing the qualified version. Clean-checkout builds, Windows archive handling and signed Mac package manifests all had to pass. The existing four frame-time profiles passed with unchanged thresholds; that is evidence for those measured profiles, not a promise about every machine.

On 1 October a player found a hole in those checks: the public short URL showed an empty map and forces panel. The engine initialized, but the server still permitted the old inline startup-script hash, so the browser blocked the script that selected game mode. A server rewrite also left relative configuration requests pointing outside the game directory. I corrected the hash, redirected the short URL to the actual game document with its query preserved, and added a release check derived from the script itself. A build passing locally had not proved that every public entry URL worked.

The same player then reported units and a Construction Yard on different sides of River Crossing. Repeating a player-versus-AI match showed why: four bridge-test patrol actors were still authored into the public Conquest map and always belonged to player slot one, while normal starting units followed the selected spawn. The correction removes those actors from the playable map. The bridge movement, repair, combat and fog checks keep their patrols in a private in-memory test map built from that same source. A new regression gate starts both player slots at both spawn positions with each starting-unit preset, checks both armies and deploys the MCV. The browser check also clicks through setup and witnesses the base on screen. A second report caught a detached upper fence rail: its posts ended below it. Extending the three saved Blender posts connects both rails without adding triangles; the other scenery assets remain byte-identical. Scenery export and freshness now run in the shared build lane too.

The next screenshot exposed a crowded multiplayer lobby. Its clearer layout keeps participants and Ready/Start separate from three collapsible settings groups, and gives temporary chat a visible panel. The real lobby controls passed, including three window widths, keyboard use, read-only guest controls and admin handover. An initial strict performance comparison held the candidate: it reported a small frame-time shift on the physical Mac. Follow-up showed that the same runtime bytes were already live, while the old 0.2 ms CPU injection was an unsuitable positive control for delivered frames. The release check now uses a calibrated skipped-frame control and a 2% margin. After the candidate passed the physical Mac profiles, the 3 October full deployment shipped the clearer lobby, the River Crossing spawn repair and the fence fix in the browser game and desktop downloads.

On 4 October I gave the game one public product version, Alpha v0.1.0, across the landing page, this story, the credits, downloads and the desktop app. The five refreshed desktop installers use the same measured game runtime as the 3 October release; the new source tag identifies the packaging and version-label changes. I checked every live browser runtime file against that build, and the released Mac app opened a local hosted multiplayer match with WebGPU rendering. Browser players can join hosted rooms; hosting stays in the desktop app.

On 6 October I qualified the Alpha v0.2.0 candidate; the full deployment completed on 7 October. Two server-side defects had to be fixed first: the public room list had dropped each room's call policy, so joiners never saw the call bar, and an outdated ticket service on the relay rejected the peer-id that mutes and roster cameras depend on. The release carried those repairs and the twelve-track score. The version rose across site, desktop and relay, the ranked gate was re-pinned through a real end-to-end match, and the four frame-time profiles held on the physical Mac within the same margin. Calls were verified between a browser and desktop on a direct route. A Windows-only test expectation surfaced in packaging and taught the lesson again: derive path expectations, never type them.

Later on 7 October I opened the public Freehop switch and corrected Community #1's room policy. The browser, both server nodes and relay agreed on the same simulation build; the installed Freehop package matched the version pinned in the project. Those checks establish what was deployed and enabled. They do not claim that the next full release is already published or that the remaining physical acceptance work has passed.

The protocol research did not stay in the worktree. The controlled direct-connection cases and the deliberately blocked routes that failed in the prototype became the 40-of-40 home-lab matrix the Freehop SDK now publishes beside its code; what once had to be kept out of the game became the call inside it. What is still honest to say: those are simulated-network results from my own lab, the SDK is alpha, and media performance beside gameplay on real home networks is the next phase, not a measured claim.

The road taught me that AI multiplies what a developer can attempt, but only when the work has a direction. Fable and I could tackle a browser port; Astra could give hundreds of models a visual language; GLM could carry a broad multiplayer build; Grok could interrogate a frame budget; Claude and Codex could challenge each other through ContextRelay. The harnesses, models, and platforms made that range possible. I still had to choose the rules, reject the wrong film, demand a working rig, and decide when a number was honest enough to publish.

I started with a memory of friends at computers in Amsterdam. Now a player can open a browser link and command the battlefield I used to imagine. The next seat can be a hosted match through the desktop app or a browser joining that room. I am still the one who has to listen when a replay, a byte count, a frame clock, or a player says I got something wrong. That is how this game became more than a generated demo.

The likenesses behind Jerome, Kirkov, Riki, and Jackson belong to family and friends who agreed to be part of the project. The full list of people and tools is on the credits page.

Redline Wars is a fan reinterpretation and is not affiliated with or endorsed by Electronic Arts, Westwood Studios or OpenRA. OpenRA and its contributors retain their rights under GPLv3-or-later. Command & Conquer, Red Alert, and related marks belong to their owners. The game's notices describe the software under GPLv3-or-later, including the WebGPU client, separately from artwork, recordings and trademarks. Copyright in Jolyn Studios' contributions does not replace those software licence terms. The credits and source notice explain the public edition's scope and the separately sourced creative material.

THE RECORD · PRIVATE DEVELOPMENT HISTORY

1013 commits.

The chapters above are how I remember it; these entries come from the private development repository. The public source edition has its own repository and release history. This curated development record shows each selected commit’s short hash, agent attribution and date: 72 days first to last, work on 38 of them, the busiest day at 109. A handful of rows stay in the studio.

  • Claude Opus 5 353
  • Claude Opus 5.5 154
  • Claude Fable 5.1 6
  • Codex · GPT 5.6 3
  • Codex · lobby and release pass 41
  • GLM 5.3 Flash 350
  • Grok 109

Grey rows are GLM 5.3 Flash and Grok, mostly review beside the GLM line. GPT worked through the Codex CLI, and Astra 6 generated nearly every Blender source in the game, the MCV included. The lobby and release pass is attributed from its recorded Codex work session.

OFF THE RELEASED LINE · SIDE BRANCHES · 829 COMMITS OFF THE LINE

Before the line

The same monorepo already carried earlier experiments on side branches, starting with Boxcade v0.2.0 on 10 June. None of it is on the released line; the off-line count also includes later experiments. Redline Wars itself starts on 28 July.

28 JUL – 31 JUL · 89 COMMITS

The line starts

The OpenRA fork, the mod and the browser bridge, the web core and the snapshot decoder, terrain, a renderer, and drawn actors. Three days later the contract rulings landed: audio is a sibling engine, and everything is generated but at build time.

13 commits
  1. d7ee6819

    steelseed: contract, tickets and tool-output ignores

  2. 68158e82

    steelseed: OpenRA engine fork, mod and browser bridge

  3. 0874310e

    steelseed/web: core, snapshot decoder, dev fixture and the gate tools

  4. f099baf2

    steelseed/web: materials texture forge, TerrainAtlas and geo generators

  5. 459005ed

    steelseed/web: renderer · sky dome, one atmosphere, blended-ground variant

  6. bd5447b5

    steelseed/web: terrain, sky model and camera

  7. 40f71e02

    terrain: do not emit cliff walls for sub-step cracks

  8. 850cad15

    terrain: taper collapsed wall normals instead of deleting the wall

  9. b85f8827

    terrain/render: fix wall-normal aliasing, fade normal detail at cliff tips

  10. f32ba96f

    docs: record the atlas gate outcome and close three tickets

  11. bce9bad4

    units: draw the simulation's actors, with faction-readable silhouettes

  12. dadca9cb

    render: calibrate clear-day probe fill to 0.5 (t1-render-7, partial)

  13. 3555cc3c

    t1-render-9: record the reproducibility diagnosis, still open

1 commit
  1. cc24338f

    docs: RESUME-CLAUDE.md · session handover

75 commits
  1. 4805e251

    plan: the graph executes itself, and it says it cannot advance

  2. 7a2ca5ef

    contract: audio is a sibling engine, not a room inside the renderer

  3. ab8663b5

    contract: generate everything, but at BUILD time · the bake is what unlocks the lighting

  4. d4954d24

    contract: pin what may and may not be taken from the RA data

  5. 570829e9

    contract: the roster slot archetype and its numbers drive generation

  6. 7f5abb3b

    plan: six new nodes from the 14.x rulings, and the ordering flips to lighting-first

  7. 1e72b5bf

    plan: close the three structural gaps, and the graph gets its first READY node

  8. 8f0bf8b7

    lighting-0: ambient dominance is REFUTED · the defect is material contrast at 28x

  9. 032bfcb6

    mod: the frontier gate becomes executable, and the project gets its FIRST falsified gate

  10. ff2707f8

    issues: de-duplicate the two ladders on the only boundary that means anything

  11. e5c9752e

    art-1: the removed science-fiction art direction is still in the shader, and rule 9.0 cannot see it

  12. 06b96709

    quality-1: the reserve line gets its first measured number, and the pricing rule works

  13. 304b22b7

    contract: register LEAD-009 (no spot lights) and LEAD-010 (probes are not GI)

  14. bc83a771

    t1-terrain-5: the atlas LAYER INDEX is the flicker source, and 12.3b says it cannot be

  15. 8f48841e

    plan: record everything measured today, and a second gate that cannot fail

  16. f00a081d

    t1-terrain-5: withdraw my "this is a bug" reclassification, same day

  17. 6fd70099

    units: vehicle headlamps as real punctual lights, daylight-gated

  18. 44a20600

    plan: 0.3 delivered, the clamp is unblocked, and one hypothesis worth testing

  19. 6ccf9757

    gfx-1: record the DPR mismatch in my own 0.1c write-up

  20. 15286543

    gfx-1: clear two stale lines the 14.9 restoration and 0.3 left behind

  21. 54c4f65f

    devsnapshot: coherent ore bodies · and the flicker did not improve, measured

  22. 06f22f85

    t1-terrain-5: record the negative result the earlier commit lost to a shell reset

  23. 1d96abf1

    gfx-1: replace the max-channel predicate I set · it was unreachable within the hour

  24. 46e39e2b

    render: clamp low-light exposure gain · night is night again

  25. 0317e3de

    docs: rewrite the handover so the loop survives the session ending

  26. 5afdba65

    artcheck: replace rule 9.0 with a gate that can actually fail · and it starts RED

  27. abc34dd2

    0.1b: decomposed · snow is 77% of the flatness, and I am not approving the obvious fix

  28. 35f9c50a

    0.1b: the separation inverts · snow is correct, everything else is too dark

  29. a095d019

    0.1b: a second lever that raises the denominator instead of lowering the numerator

  30. 67311d74

    0.1b: snow distribution hypothesis refuted · it is placed correctly

  31. b14a30b8

    0.1b: snow distribution closed, and the palette is the answer by elimination

  32. a5c7ba57

    artcheck: review gravel and grass · 5 of 16 generators now actually read

  33. 08d8c434

    art-1: sharpen the rewrite spec by reading the sibling that is correct

  34. c06601e5

    artcheck: all 16 generators now actually read · review debt cleared, one real defect left

  35. 5579c36c

    0.1b: lever 2 downside confirmed, and the decision rule set before the curve

  36. 90884bef

    todgate: the sky gate becomes executable, and it independently reproduces the clamp result

  37. 3f257ed4

    materials: raise the dark surfaces toward measured reflectance · flatness down 35%

  38. bb0c9288

    plan: rung 0 is complete except sky chroma · the entry point is cleared

  39. 330c7a50

    todgate: fix my own DPR-1 mistake · the same one I was shown two hours earlier

  40. f59e1a11

    0.1c: correct the DPR-1 figures I committed as DPR 2 at e334346

  41. 29ab5ab7

    todgate: promote to both halves of the predicate, at DPR 2 that cannot be faked

  42. 676e2c5a

    plan: falsify the sky gate · third witnessed red in the project

  43. 8c8cd7cd

    sky-1: close the falsification probe so it does not read as an open defect

  44. ce846248

    art-1: I documented one third of this violation · correcting the scope

  45. 87072f10

    artcheck: my own gate covered half the surface the violation lives on

  46. 8aa37549

    artcheck: --update-hashes could silently claim review for functions nobody read

  47. 2982e805

    0.1b: lever 2 mechanism understood · snow shading IS occlusion, and 0.20m starves it

  48. 51ea79fe

    materials: rewrite the Lattice to the art direction it was supposed to have

  49. 095cb38d

    art-1: a FOURTH copy of the removed art direction, in a node nobody scanned

  50. 55ba38fd

    plan: harness stopChild leaks processes · one root cause, two files

  51. 51796ec0

    artcheck: GREEN · all 32 halves read, and I paid the debt I created

  52. bc6fdb27

    artcheck: scan geo/ too · my replacement for a word-grep had a narrower scope than the grep

  53. b5a1489a

    art-1 CLOSED: the fourth copy is gone, and the gate caught two bugs in itself

  54. 92d753a0

    tools: own spawned trees as process groups · the wrapper exit was never the right signal

  55. 6f7fcee4

    plan: fourth witnessed red · and the first VERIFIED node is now one gate away

  56. 58069912

    plan: re-run tools-sim so the cache stops reporting a red I created on purpose

  57. 45381cf2

    docs: bring the handover current · rung 0 closed, and the traps that cost the most

  58. 49dba8ac

    docs: fix a stale absolute in the handover I wrote ten minutes ago

  59. d548557e

    fix(steelseed/tools): all four browser tools share one cleanup path

  60. 11b29610

    plan: tools-sim is the first node ever to derive VERIFIED

  61. 47d278bd

    plan-1: 64 of 87 gates cannot be evaluated, and that is why nothing settles

  62. 018a9e5f

    materials: VRAM was under-reported by its largest allocation, and now has gates

  63. 95d96663

    docs(steelseed): bring the handover current, and stop restating counts

  64. 86c37e00

    mod is the second VERIFIED node, and promoting it exposed bridge

  65. a2f32855

    core: §0.2's "ctx.rng bit-identical across runs" finally has a gate

  66. 62280ec0

    docs: the sim-integrity gate measures emitter parity, not renderer parity

  67. 7f96e578

    docs(steelseed): five gate tools, and the two rules they converged on

  68. 6870af79

    docs: the dangerous measurement failures are the ones that return green

  69. 1e3c75ca

    fix(render): pin the provoking vertex · bare @interpolate(flat) means "either"

  70. 1b219c04

    docs: t1-render-9's real finding is the methodological wall, not the bug

  71. b20ef293

    t1-render-9: cause NOT FOUND, and the methodology is the finding

  72. 3575cc10

    bridge: the snapshot layout gate found a live C# contract violation

  73. ad00eff5

    engine-1: the browser build cannot complete, and a prebuilt bundle hides it

  74. 6cf58d4b

    Four human rulings land: WebGPU required, motion vectors in, denylist by extension

1 AUG – 11 AUG · 144 COMMITS

Finding the look

Art direction v2, near-future clean industrial. Archetype shapes, gates that measure instead of reassure, OpenRA parity closed with the assetless runtime gates green.

76 commits
  1. 2c7336e7

    perf: the target workload is measured for the first time, and headroom shrank 37%

  2. 559f4c7f

    art direction v2: near-future clean industrial, and the reviews had no version

  3. 36ec860e

    render: two transposed matrices tinted every pixel in the game blue

  4. ab0a7021

    geo: rotateZ on a Z-aligned primitive is a no-op, and it shipped two visible defects

  5. e3c37a6c

    t1-materials-3: player colour covers the whole unit, and the numbers disagree

  6. eedca793

    archetype: the tuning numbers become geometry, and a gate that refuses N identical machines

  7. 9bf2c0e5

    docs: handover records the direction change and two invisible defects

  8. 7dccb27c

    correction: units are not triangle-bloated, and I divided a per-pass counter

  9. ff7f7f78

    archetype: the first family generator, and a silhouette gate that caught its own instrument twice

  10. afb38711

    archetype: wheeled, plant and emplacement · and the silhouette gate was measuring a slice

  11. f1406d3d

    archetype: infantry, the real-roster adapter, and the gate fails on real data

  12. 9604d9f1

    archetype: the footprint mask was thrown away, and power was read only for its sign

  13. 47b79bf1

    archetype: aircraft · every actor in the roster now generates, 0 skipped

  14. da80ac30

    archetype: a contact sheet, and looking at it found four things reading alone did not

  15. de4a7656

    archetype: weapons reach geometry, and two scale errors they exposed

  16. 606c568b

    correction: f392f2d said "all gates green" while rostergate was red

  17. 9c31306d

    units: the roster's own hulls reach the screen · 44 types, not 5 classes

  18. 3da8c459

    tools: profile was fine, my Playwright call was not · and three review fixes

  19. 3bfcd317

    materials: the mask was correct all along, and the number was the problem

  20. cb35ebd4

    render: service paint, and the legibility traded for it is a number

  21. 683864a2

    tools: unitlook · measure units only, because I could not judge my own work

  22. 7d7a43ab

    materials: units were 2.9% brighter than the ground they stand on

  23. 7326b888

    docs: the roughness trial is not a result · the baseline was moving

  24. 5b35afc1

    archetype: the faction contrast reaches architecture, and buildings stop being one shape

  25. f59390d0

    archetype: the turret was the size of its own tank, and the bug was dimensional

  26. 76fbd584

    archetype: the rotor was a lid over the aircraft

  27. de5960dc

    bridge: a JS order moves a unit · the ABI was unimplementable, and now it works

  28. f974d514

    perf: the reference workload got CHEAPER while gaining 39 actor types

  29. dc621126

    archetype: greeble is invisible at gameplay zoom, and the measurement says don't ship it

  30. 1a3436a9

    docs: session summary · what holds, what I got wrong, and where to look next

  31. 0c66ed1c

    terrain: "reads flat" finally has a number · dynamic range 1.983x

  32. e8f53c68

    bridge: the decoder now churns zero objects per tick, and the cache key is the heap epoch

  33. 21611a65

    terrain: the flatness decomposes, and the answer overturned three hypotheses including mine

  34. 784df956

    terrain: the corduroy was never aliasing · it is downward normals losing sun AND sky together

  35. bb6a83dd

    terrain: SH-L1 ringing refuted too · the bounce arrives, it is just dim

  36. c98f094c

    art: the Foundry was wearing desert camouflage · chroma spent, luminance kept

  37. 44ad3243

    tools: unitlook's contrast number moves 40% with workload, and it decided a colour

  38. b382604e

    art: emissive specified · the only surface treatment that survives the zoom this game is played at

  39. b900a1b8

    tools: both roster-review instruments were blind, and that is why 16 buildings are the same

  40. d323440c

    archetype: structures were on a tank's toughness scale, so 16 buildings generated as 7 heights

  41. 5a7f5795

    archetype: four massing archetypes, so a silo stops being a warehouse with a different roof

  42. 93d79b18

    archetype: the infantry were standing on pedestals, because the stance never opened a gap

  43. 8925c18f

    archetype: the tracks were a string of beads, and every tank had exactly four road wheels

  44. db6c42ca

    archetype: the wheeled chassis had a frame and no machine on it

  45. 9bb660f1

    bridge: the managed floor is 248 measured bytes, pinned exactly and red in both directions

  46. a29959d6

    archetype: a wing aircraft's fuselage was being thickened by its own wingspan

  47. 9e45d8b6

    archetype: an emplacement was a round tower, which is now what a silo is

  48. d6c4c4ae

    archetype: the eighth family · a vessel, built from the waterline up

  49. bddd0c63

    mod: a naval domain · 44 actors to 54, and the vessel family has real ships

  50. 79329217

    mod: the roster reaches 97, and the clamp that ate infantry identity is the fourth of its kind

  51. 6115fa69

    render: functional emissive, stage 1 · 1.741% of unit body at 4.525x the lit hull

  52. b8cd0a53

    archetype: elevation is the anti-air cue, because 3 cm of extra barrel is not a cue at all

  53. 07893e3d

    render: emissive is actually ON · correcting a commit whose message measured a build it did not contain

  54. 96e9b9ba

    archetype: the separation gate could not tell a silo from a hall, so it was measuring the wrong roster

  55. 316a0aab

    mod: the infantry barracks was generating as a storage tank

  56. e83664b5

    mod: aircraft facilities get an apron approach, and the parameter metric keeps improving while the silhouette does not

  57. a0216107

    issues: the aerie/mandala collision is on the ridge, not the mask · diagnosed, tried, reverted

  58. e3372186

    archetype: the roof was hiding the footprint, and the camera only ever sees the roof

  59. 22bddf71

    issues: the last silhouette pair is a coincidence in the mapping, not a defect · with the numbers

  60. 9c36056c

    tools: the silhouette check reported ONE pair, so I spent a session believing there were two

  61. c1d06ee0

    issues: 26 plants share 4 massing archetypes, and cost is being spent on a treatment that does not read

  62. f864843b

    archetype: the roof plant was SEEDED, so it encoded the instance and never the building

  63. d3e2648c

    render: bloom keys off the emitter, not off brightness

  64. 7ce30aa1

    render: the ground had see-through holes because two shaders disagreed on depth

  65. 66c571e9

    gate: the depth agreement now has an instrument, and it needs all eight jitter phases

  66. 3ec27624

    archetype: anti-air was a guess, and the weapon never reached the mass

  67. a73be3f5

    gate: the silhouette frame was DERIVED from the roster, so one actor rescaled everyone

  68. ee44050a

    archetype: 26 plants had 14 footprints, and the footprint IS the silhouette

  69. 7222a696

    units: eleven ships were never generated, and the gate counted them as "skipped"

  70. 4206db43

    units: the mesher box was a guess, and it cropped 29 actors and deleted one

  71. 0c59c9e3

    geo: the rig thought Z was up, and every turret would have pitched over

  72. 29bec24b

    render: the three spare instance scalars are claimed, before three features race for them

  73. 71539c06

    archetype: §14.13 was working, and that is why every actor looked the same

  74. 3335d93b

    units: every vehicle stood bolt upright on hillsides, and no aircraft ever left the ground

  75. a74d25b6

    materials: 97 actors shared two appearances, and the shader had been ready the whole time

  76. 5cc101b5

    tools: motion was unfalsifiable by construction, and now it has three instruments

51 commits
  1. 2d3f066d

    archetype: eighteen actors carry a hand-drawn plan, and the generator never saw it

  2. 083047f4

    archetype: all seven families now say what they are made of, and plants build on their plan

  3. 6bda4db2

    archetype: sixteen infantry shared one body, and the armour they carry was invisible

  4. ea22e21f

    archetype: the authored plan reaches vehicles, and prong/pylon stops being a collision

  5. 418c04ce

    render: the skinning contract, and the depth agreement survives it

  6. c9be0315

    profile: every performance number in this project was measured against five actor types

  7. 28693f79

    render: the bone palette was off by one, and the turret matrix would never have been read

  8. a6753d4d

    profile: the instrument was measuring itself again, and I published the conclusion first

  9. 2d38ebb9

    units: the turret turns · the first thing in this game that has ever moved

  10. 23797624

    docs: §12.2 carries the skinning contract, now that something is using it

  11. f30b02b3

    profile: static terrain was rebuilt sixty times a second, and it cost nine milliseconds

  12. 0d1f566c

    anim: the node that owns the one thing units is forbidden to remember

  13. fb3fb0c3

    render: track scroll is a UV phase, not a bone · the render half

  14. a3e80e74

    render: a machine at 5% health looked factory-fresh · the damage half

  15. 98075832

    units: actors stopped snapping at 25 Hz, and the gate that watched them was watching itself

  16. 4cc84a0d

    render: the damage channel reaches the instance record

  17. a3564bc2

    units: the three pinned channels are driven, and nothing is inert any more

  18. b60a93fb

    units: wheels turn, and one actor's are too small to · which it now says out loud

  19. b32ba131

    units: sixteen soldiers walk, and the phase comes from the ground rather than the clock

  20. 921ae1b6

    archetype: the mod says what each weapon is FOR, and nothing had ever read it

  21. 09150369

    shroud: the node exists, and every player could see every enemy base until now

  22. 842a1598

    render: LOD selects per instance, and the level is shared or the ground goes see-through

  23. c6aa546c

    ui: a camera you can fly over an army you cannot touch is a diorama

  24. 7ea52ca5

    shroud: the ground was lit everywhere, so the enemy base was never hidden

  25. 5d095657

    archetype: three transfer functions were destroying the facts they derived from

  26. 59b612cb

    gate: two comments claimed the guns were aimed at the shot, and nothing was

  27. 9319444b

    gate: muzzlegate was measuring the wrong thing, one commit after it was written

  28. 4c461039

    archetype: the mount goes where the shot comes from, and six actors had no gun at all

  29. 8b49d5c9

    archetype: a soldier was taller than a tank and twice the height of a warship

  30. 4541436d

    archetype: two aircraft were the wrong kind of aircraft

  31. f667a883

    roster: the committed table had drifted from the mod, so every shape encoded a dead ruleset

  32. 56bdefa6

    roster: the VTOL fix now actually reaches the screen

  33. 3ea50235

    roster: Family.fixedwing was defined and never assigned

  34. 0d173561

    bridge: §4.9 events cross the boundary, and fx stops being blocked

  35. ffa6a097

    audio: the game has been silent since the first session, and damage is what a gun sounds like

  36. d3ad7d39

    fx: the game had no visual effects, and a shot now leaves the machine that fired it

  37. 6303b607

    audio: vehicles moved in silence, and the ground they move over is what they sound like

  38. 1ecbdc3e

    audio: the half that decides WHERE a sound goes had no instrument at all

  39. a9ce3bf9

    audio: your factory finished and nothing told you

  40. 35238cd6

    docs: resume handover · fx and audio ship, and the two numbers a successor needs

  41. 464c497f

    render: effects lit like painted steel, because nothing in the shader could emit

  42. 671b0d5f

    archetype: a factory and a silo drew as the same building, and I twice called that your decision

  43. f81a959d

    render: Lattice's emissive read as zero, and once it could be measured it was also wrong

  44. 15a57b9f

    archetype: infantry never read how many barrels the mod gave them

  45. 04911f15

    rostergate: I said this red was an instrument problem, measured it, and it was not

  46. 89391813

    archetype: INFANTRY_OVERSIZE was "the owner's call" for four sessions with no numbers attached

  47. 287ab3f6

    fx: an instant hit had no flight to draw, so the tracer is drawn from the two events that bracket it

  48. f6fd620d

    docs: transfer STEELSEED coordination to Codex

  49. c9f25adc

    feat(render): add per-object motion vectors

  50. be28c3bd

    feat(ui): add authoritative production and economy HUD

  51. bfcc8e67

    feat(fx): render authoritative destruction bursts

1 commit
  1. 14a30685

    feat(render): persist authoritative battlefield wrecks

16 commits
  1. 7c16ec99

    feat(core): expose authoritative match lifecycle

  2. 93eaa885

    feat(ui): complete the first-playable match flow

  3. 779bc574

    feat(forge): bake one deterministic mesh offline

  4. 26a72b7c

    fix(render): preserve authored night exposure

  5. 41a210c6

    docs: record the first-playable release boundary

  6. cfee6b5c

    Integrate assetless OpenRA Red Alert skirmish

  7. 74feef97

    Close OpenRA parity and assetless runtime gates

  8. 14adadd1

    Fix edge-spawn camera and terrain visibility

  9. 323f93e7

    Fix skirmish focus, panning, and actor grounding

  10. 1556fee5

    Complete authoritative RA controls, fog, and water roles

  11. ad854df4

    Add unique OpenRA visuals and validated placement

  12. e9c888ce

    Make OpenRA production and economy discoverable

  13. e21ceb12

    Add RTS drag group selection

12 AUG – 4 SEP · 0 COMMITS

The quiet weeks

Twenty-four days without a commit on the released line. The strip keeps the gap on purpose: the record includes the waiting.

5 SEP – 11 SEP · 158 COMMITS

The battlefield gets real

Authored ground, meadow, canopy and tank sources checked against the visual reference. The Tesla coil gets its bolt, muzzle flashes are sized by the weapon that fired them, destruction happens in stages. At the end of the week: the first soundtrack, and the first real SFX banks.

2 commits
  1. b1d92a63

    Playable relief, out-of-bounds scenery, quality tiers, and an AOT-fast host

  2. 20030de5

    Fog as mist, faded selection rings, harvest orders, wider right-click targeting

74 commits
  1. b6a3e63d

    Allow pinned free art sources and verify download provenance

  2. 14bff441

    Author the ground, meadow, canopy and tank sources against the visual reference

  3. 94422c72

    Make night playable, the click decide the order, and every projectile leave a trail

  4. c95f1b81

    Make the anatomical soldier the rifleman, instead of an experiment behind a flag

  5. 96733528

    Record where the visual work measurably stands, and the order of promotion

  6. 0711b194

    Raise the mountains to the human's rule, and author the rocket soldier's launcher

  7. eb5d2759

    Draw the shot: lift effects onto the drawn ground, size tracers in pixels, let an emitter emit

  8. 2b403121

    Make firing read as a round, not a laser

  9. 02cf5a5d

    Break surface boundaries off the cell lattice, so ground transitions stop reading as squares

  10. 6f6e065b

    Record the refinery, crane, radar dome, rotor and aircraft-sourcing requirements

  11. 3262673f

    Record why rotors stand still: the disabled flag is set by any disabled trait

  12. 2bdc1b2c

    Stop the mid-match freeze, and make a dead simulation say so

  13. 0cf7822d

    Fire from the barrel, not from between the tracks

  14. 2ab5947a

    Record night lighting, the fog reveal bug, and mark the freeze and muzzle fixes done

  15. 9f3c4bcd

    Diagnose the drive-through-buildings report: the models overhang their own cells

  16. 6bc8bb62

    Record that 277 of 281 assets render as flat colours, not materials

  17. c412b0ec

    Roll the surface promotion out past its two-actor pilot

  18. 915c016e

    Publish the shroud grid from exploration when fog is off

  19. 8fff06f5

    Author a six-rung condition ladder for the power plant and heavy tank

  20. 522f8b4e

    Update the register: fog and minimap done, staged damage started

  21. a3cf765c

    Diagnose the dead cavity channel rather than leave it open

  22. 14d2bd23

    Drive the harvester into the refinery, lift the crane off the roof, build a real radar

  23. 0cbb2da5

    Turn the lamps on at night, off the anchors that were already there

  24. b937aed3

    Mark aircraft, refinery, crane, radar, night lights and materials shipped

  25. 77d908ba

    Give every aircraft its own airframe instead of one body with three branches

  26. a9cf9e71

    Let the rotors turn: the disabled flag does not mean the machine is off

  27. 9bdef23f

    Witness the rotors turning, with the number the angle is derived from

  28. c550b1e6

    Close item 26: the rotor freeze is diagnosed, fixed and witnessed

  29. 3d667bff

    Teach the source gate that a damage state is not an actor

  30. db3ac39d

    Stop throwing away four fifths of the baked ambient occlusion

  31. 7b01bbad

    Record the clamp that swallowed 40% of the baked occlusion

  32. b8bee90c

    Assert what contact shading being local actually means

  33. b711b2bf

    Count a bare identifier as a computed RNG stream name

  34. fe082816

    Give the rifleman arms of the right length, and something to do with them

  35. fd40e0c0

    Re-pin the weapon bindings to the regenerated RA catalog

  36. a5a26ff3

    Close items 9, 18 and 27, and record where aim and recoil stand

  37. 70bec457

    Record emissivegate and todgate red, with what is known about each

  38. 10f14a4f

    Separate the two halves of the squares complaint

  39. d6bf9ee1

    Put the refine step in the documented build sequence

  40. e4f56683

    Put the team colour where a top-down camera can see it

  41. 63bc0a98

    Record why faction markings were invisible: they face sideways

  42. 3b9380b4

    Record that playtest waits on a symbol the shipped page never defines

  43. 9cb56194

    Remeasure the roster table; the old denominator counted a mod we do not run

  44. e3a311fa

    Register the human's latest seven items, with the audit behind them

  45. 60022eaa

    Play the aim and recoil clips, and find out they are 5.5 pixels tall

  46. 432c8188

    Size a muzzle flash from the weapon that fired it

  47. 724155d8

    Register the scenery flicker, the unlit asset and the APC

  48. 8df90746

    Spend the scenery budget on what is nearest, not on what the scan reached first

  49. 64591c3f

    Select a damage rung from health, and let a wreck be the ruin

  50. bd986c16

    Give the roster flags, wind and a bigger radome

  51. f9eabad0

    Write a handover, and record where the rate limit stopped everyone

  52. fb7962c1

    Revive the forge, which had been dead and saying nothing

  53. 41c66350

    Carry authored smoke stacks out of the .blend and into a frame

  54. 4310f6f7

    Withdraw item 40: those pairs are decoys, not factions

  55. e1ba96da

    Reopen item 40 on the premise that is actually true

  56. 16c14590

    Register the four missing effects, and what blocks each

  57. 897acde9

    Give the Tesla coil a bolt, drawn to its rules range

  58. c0f86a87

    Stop the scenery vanishing, and say what was actually wrong

  59. f682eab6

    Ship the rebuilt roster: factories, barracks, rockets, damage, explosions

  60. 04c09d46

    Make the world end in country, not in a bathtub rim

  61. 51d1449c

    Bring the register up to date: 61 items, 3 still open

  62. 47231d71

    Give the two armies different machines, and find the triangle budget doing it

  63. 5404c661

    Finish the faction templates, and repair two things they exposed

  64. 9715f499

    Give every weapon its own voice: the bank had eight and the game played one

  65. 1a827886

    Make a tank fire like a tank, and stop the tesla burning orange

  66. 2bc0f947

    H goes home, T pairs zoom-out with tilt

  67. 412e9e14

    fixes

  68. e03b27a2

    Fix the black 3D pass, ship staged destruction deaths, merge the tank-realism showcase

  69. fed00694

    Give the yard, MCV, truck and tanks the war factory's industrial language

  70. feccabed

    Play an animated unfold when an MCV deploys into its construction yard

  71. 575aaac5

    Make the fixture's deployable selectable instead of a hardwired first actor

  72. 77e29106

    Navigate by heading: compass tape, top-right translucent minimap, map cardinals

  73. 43ec2b9a

    Null-safe the devtypes fixture override parse

  74. 3b7e9377

    Drop scratch screenshot scripts committed by accident

37 commits
  1. 4a04b9b0

    Stop the manual chunk map from cycle-freezing the production boot

  2. 8ce99f73

    Re-author the construction yard's damage ladder over the industrial pass

  3. 853efbbd

    Ground every authored detail: kill the floating-parts defect at revision 2

  4. 84369be9

    Move the strategic overview to the top-left

  5. 4d90826a

    Add BUY and SELL to the production panel

  6. 83bcd090

    Attack cursor and hostile hover brackets

  7. 7846c954

    Wheeled vehicles leave tyre ruts, not track shoes

  8. 28e1ec8e

    Surface the full managed stack in the halt banner

  9. 627cab4c

    Give the death-combat witness a window that outlives the first corpse

  10. d6d21536

    Single-surface ground joins the macro field

  11. 03e82977

    Revert "Single-surface ground joins the macro field"

  12. 1b41c97b

    Soil pays the macro field

  13. 3a55f116

    Instanced near-camera grass

  14. c5181ec7

    Leaf translucency for foliage cards

  15. 1998d010

    Make the grass tuft rig-less

  16. ebe971f7

    Denser crowns and an understory shrub layer

  17. adf1b50a

    Scorch decals under explosions

  18. 76900722

    World props: rocks, fallen logs and fence runs

  19. 3e7608da

    Cloud shadows on the ground

  20. 5c286812

    The prop sources the previous commit's scatter points at

  21. 108c2e14

    Wind sway on the near-camera grass

  22. c3ec1ad1

    Catch forgegate up to two loading contracts

  23. bc69bc6a

    Add the WIP diagnostic behind the TAA velocity question

  24. 2728af1f

    File the object-velocity A/B instrument precisely

  25. 7ff71394

    Record the object-velocity A/B measurement honestly

  26. ab89b31c

    Gate the per-object velocity path by integrated diff

  27. 336f4ac4

    Foliage-aware contact pass

  28. d2337211

    Colour grade after AgX

  29. 988bc047

    Put the skinned wind tuft behind the grasswind flag

  30. 6b75188e

    Assert per-object velocity in the motion gate by reading the AOV back

  31. 4dd3c062

    Keep the local composed-build server as a tool

  32. fa9f99bb

    Shorten the tread shoe below the stamp pitch

  33. ae6bf6e6

    Add an interface minimise toggle: the H key or the corner chip

  34. 357c5129

    Pin the wreck visual witness to tree spawns

  35. b76dcf85

    Fix the stale ragate stubs and move the HUD toggle to I

  36. 22701077

    Make the wind amplitude part of the grasswind test switch

  37. 61aa4646

    Move the camera pad beside the map, add per-panel minimise

1 commit
  1. 7bb86fdc

    stable

19 commits
  1. c56cc308

    net-1: two-browser lockstep gate passes (browser WebSocket transport, dedicated launcher, host Join* exports)

  2. 611acea3

    mp: composed-bundle network session driver (mp-host-fullgame.mjs)

  3. e49f7207

    mp: per-client shroud isolation gate (PR 2)

  4. af434b7c

    mp: LAN room host + real session-UI network flow (PR 3)

  5. 726e77e4

    mp: public spine gate (PR 4, WIP · paused mid-debug)

  6. 8ba023a4

    mp: 2-human rooms wait for all players (PR 4 WIP, paused)

  7. 0184fc9a

    mp: public spine gate green (PR 4)

  8. 3446f422

    mp: flip network transport to available + real-UI desync gate (PR 5)

  9. b3d2daf7

    base: UI/UX + art wave (renderer, units, fx, sky, audio, blends, docs)

  10. dc289a35

    merge: multiplayer service (PRs 0-5) into the base UI/UX + art wave

  11. 15d6d767

    merge fix: restore the outcome-title element binding lost in the wave merge

  12. c68ffbbb

    roomhost: --host bind flag for LAN play between devices

  13. ca1abd1c

    mp: federation · spine + dial-out nodes (geo routing, tunnels)

  14. b888d9e1

    node: distributable node packager + spine upgrade diagnostics

  15. 4f17aca3

    node: status GUI + assets-url baking in the npm installer

  16. 1678bd2c

    hud: header cleanup (power bar, minimap stats, camera expand) + network-status line + local-sky note

  17. 28b11eb5

    mp: player HUD probe + bridge wrapper (bottom-left panel data)

  18. ecab4170

    mp: lobby tabs + room browser + validation fixes

  19. cbc1499f

    docs: networktopology.md · components, roles, LAN/federated flows, packaging

25 commits
  1. 719de651

    ui: room list scrolls (no overlap with host button), nickname required

  2. 1376cbec

    mp: idle-room reaper, ping signal bars, lobby polish

  3. 90564f5d

    ui: restore session modal wrapper; menu opens without snapshot; roster above hide chip

  4. ed18d762

    mp/ux: menu binding, honest construction voice, room-mode crash semantics

  5. 0dfdc994

    mp: numeric ping end-to-end (server sync -> probe -> roster)

  6. 0aa2590a

    docs/naming/ux: truth sync, Daalforge Studios, halt diagnostics, faction voices

  7. 5b349bf3

    ui/voice: Building moves to production start; roster under minimap; camera pad hidden

  8. 2c748cc5

    hud/voice: Construction complete fires at 100%; left column; online enemies

  9. 4b436432

    chore: remove one-off production-event probes

  10. 4e65c569

    voice: EVA is always a man

  11. 7692f310

    sec: P0/P1 relay hardening per independent security review

  12. 613bc392

    voice: rendered faction banks (allied + soviet)

  13. 0c187761

    federation: joiner drop fixed · spine owns room endpoints; all-ready start; per-faction voices

  14. d8ec2d8b

    audio: SFX bank-first infrastructure (provider pending)

  15. 47422a5e

    voice: soviet family plays its voice bank (bugfix)

  16. f2e7729d

    sfx: real faction SFX banks via ElevenLabs sound generation

  17. befe0003

    voice: faction bank wins · russia speaks Russian again

  18. d85b200d

    audio: boot-to-match soundtrack, mute-anytime (HUD chip + M)

  19. 0b6c0ff5

    menu: music toggle + volume in the game menu

  20. 3629ccc4

    mp: host gets the full skirmish options grid; joiners pick faction/team

  21. 67902fef

    ux: night mist thinned, skirmish-first tab, simple graphics labels, music menu fix

  22. 9bca713d

    ip: in-game commando renamed to Rikie (display names + fluent)

  23. cd8641f6

    mp: live lobby slots (faction/team/spawn per player), server colors, spawn host control, ambience inheritance; disconnect defeat trait

  24. 97101b76

    sky: World weather mode · skirmish syncs to real regional weather

  25. 810f9e5e

    ui: mp host options grid takes the full panel width (stray div removed)

12 SEP – 20 SEP · 185 COMMITS

Voices, films and the completion wave

The four character films land with the Riki integration, Jackson begins to speak, the tactical map arrives, infantry death voices become a full bank, and the LOD-cache performance wave merges. The network contract cuts over to /v2 and the legacy paths die.

5 commits
  1. 26cd8b67

    landing: Fable created the openra-web port (not reviewed it); why-letter type size down; blog act-2 wording aligned

  2. 4fa2aa6a

    checkpoint: planx remodel + landing art

  3. 7ed7a2ec

    grok review: publish isolation, ABI truth, HUD credits, honest copy, GPU gate, regenerate guard, gitignore, MP/census docs

  4. 36c99a66

    grok review: untrack blender backup snapshots (ignored by policy now)

  5. 76f8ff12

    grok review: snapshotabigate actor tail stride 9 planes matches live emitter

3 commits
  1. 05a3ffd8

    checkpoint: Riki integration, remodel outputs, and landing catalogue

  2. aaee2eba

    feat: integrate air/naval presentation with completion main

  3. 3ee446f9

    landing: present repaired Riki face-forward

41 commits
  1. edb3856c

    landing: explain the snapshot ABI gate

  2. 1b2b6617

    network: live node capacity, slot advertisement and the /network dashboard

  3. 0fb1b32e

    quality: Turbo 60, an opt-in 60 fps target tier

  4. 579a6887

    render: quantized unit vertex layout, geometry GPU -41.2%

  5. 233324ba

    boot: damage ladders load in the background; service worker caches the art

  6. 0baf13ab

    copy: honest stats and desktop-first positioning

  7. 6a493d39

    tools: spawn vite directly, immune to the workspace glob

  8. 22115ba8

    Merge origin/main: landing snapshot ABI gate story

  9. 4dfcf7d9

    copy: numbers table catches up with the optimization night

  10. 71f33973

    blog: what I learned grows four AI-collaboration teachings

  11. ef1aa118

    blog: restore lessons 4 and 5 lost to edit drift

  12. 17c7e0a2

    landing: Network status link in the footer's Play column

  13. 90959ee8

    game+landing: network status link in the lobby; leaderboard becomes its own page

  14. 6a31849a

    brand: Jolyn Studios everywhere Daalforge Studios appeared

  15. e93ac232

    sky: World mode also sets the time of day from the player's local clock

  16. 059138bd

    Revert 'render: quantized unit vertex layout, geometry GPU -41.2%'

  17. 7fc817c9

    units: Turbo 60's governor no longer switches the living system on

  18. 7cae56de

    audio: denser platoon-march footfall clips for the infantry gait loops

  19. 612f896e

    sky: Auto time of day follows the player's local clock in every mode

  20. 26912e08

    game: World time-of-day choice, FPS in the HUD counter, bottom placement

  21. c55a1494

    game: lobby fits small viewports, World time-of-day choice, quieter turbo extras, ore-truck and sand clips

  22. 1a625315

    tools: ore-hauler and sand footfall prompts; profiler keeps the nofog diagnostic

  23. f92324a9

    tools: profile benchmarks keep fog of war on by default

  24. 1e68b6ee

    game: gait loops off, green smooth selection rings, green local slot, honest Start state

  25. 4a593eb2

    audio: real recordings for the air/naval engine beds; markers behind the HUD

  26. e929afae

    hud: stack panels and actions above the world-marker overlay

  27. 244b8263

    blog: the Riki repair chapter and lesson eleven

  28. 84de537a

    story: soften the AI claims, own the Riki export, drop rolled-back claims

  29. dd740ff5

    game: tactical map (M), troop flash (L), select-all button, green rings

  30. c3c30391

    game: moving-troop fps · track stamp budget + camera cull

  31. d183c4c0

    Revert "Revert 'render: quantized unit vertex layout, geometry GPU -41.2%'"

  32. e9f9ac02

    render: re-land quantized vertex layout with f32 terrain pipelines

  33. 8f97061d

    game: allied grenadier speaks as Jackson (ElevenLabs bank)

  34. a6bd42f7

    game: replace the all-troops button with Ctrl+A

  35. cfd34f16

    engine: optional TLS mode for the browser test server (--tls)

  36. 440bbaa3

    game: enforce the unit shadow-caster budget (localShadowCasters)

  37. 14a7e539

    tools: march probe reports caster budget, shroud cull, quality override

  38. b6814c70

    game: low quality runs without weather particles

  39. 05c313f2

    web: wgsl smoothstep edges must ascend (spec validation)

  40. bfe12b73

    desktop: electron shell with preload landing (phase 1)

  41. b1c37b40

    desktop: centered landing, queued start, settings menu, app icon

12 commits
  1. 36b130f0

    desktop: hosting opt-in, selftest menu exercise, zero-error run

  2. 442836d9

    desktop: single-instance lock + clean landing close semantics

  3. 105f4724

    desktop: selftest verifies the lobby with game options renders

  4. cd9cc6a4

    desktop: restore verified two-window boot flow

  5. ceba1a09

    ui: simpler quality labels across the app and the browser variant

  6. 38fec0d4

    desktop: working multiplayer host flow, sound toggle, session pinning

  7. 81b8644a

    packaging: self-contained SteelSeed node inside the mac bundle

  8. bf3d237c

    engine: browser test server back to plain http

  9. 1044a854

    desktop: landing is the boot surface, explicit game swaps, portable package

  10. 5bcc695a

    desktop: game adopts the landing frame and fullscreen on swap

  11. 96fd62e4

    desktop + ui: back to main reloads the engine behind the menu

  12. d4e65870

    desktop: multiplayer joins a server entirely inside the app

13 commits
  1. 2025c401

    host: simulation boots in a dedicated Web Worker

  2. e531f3d4

    web: bridge API becomes async where the worker round-trips

  3. 47886daa

    host: ship sim-worker.js in the publish deploy set

  4. ab0e54a0

    host: tick and snapshot-emit timing evidence

  5. 314a54b1

    units: environment placement scan runs on cadence, not every frame

  6. fc45947f

    ui: marks reproject only when tick or camera moved

  7. 17a9a0cf

    web: moveperfgate · 30 fps movement gate over the worker host

  8. 28666401

    units: type resolution waits for the async actor-type table

  9. 0f5dbb86

    worker: one-shot terrain.static survives the snapshot pipeline

  10. 763c314d

    web: gates follow the async bridge and the worker host

  11. 5eae3f58

    docs: worker host, snapshot integrity and movement perf in ARCHITECTURE

  12. 17c03323

    server: content-hashed assets cache immutably

  13. 653970f7

    web: new-world intake waits for the actor-type table

4 commits
  1. 188f4575

    Multiplayer hardening: bounded relay writers, private listener defaults, admission gate, fail-closed overflow, tick-indexed desync proof

  2. ff6cba81

    Gate harness repairs: align stale fixtures with current contracts

  3. 5b9eac46

    Desktop home-hosting modes, relay registration, and shell/web boot polish

  4. de7ad1d5

    lod-cache experiment salvaged from the steelseed-gpu snapshot

8 commits
  1. 0d9b2f5d

    Open-issues backlog: order-input fixes, multiplayer client-index resolution, audio personalities and gated beds, cliff/void/contrail/mushroom/SSR/skin visual fixes, lint:rules zero, validation-only gate entrypoints, harness fixture repairs

  2. 14cde1ae

    Remove accidentally staged build output engine/bin-standalone from backlog commit

  3. c3a5674e

    Landing voice previews for new under-attack persona lines

  4. 56a7bedf

    Gate-tail round 1: manual boots deliver a decoded world (ensureFirstSnapshot), terrainStatic carries across ticks, weather pools empty on transition, fixture cadence/flag/displayTypeId repairs, manifest-relative forge pins

  5. 40335ed0

    Gate-tail round 2: status-strip uv1 encoding unclamped (bloom emits), fx zero-instance submit guards, fixture boot handshake + clock pinning + set-growth LOD allowance across tod/depth/lod/bloom/fx/anim

  6. 1ea1f6e2

    Gate-tail round 3: assetgate reconciled to shipped architecture (same-origin bank fetches, sha256-pinned pack catalog, provenance hosts, engine multiplayer transport), hero-bridge expected-side in ruleparity, mapfill camera bands refreshed, meadowdistribution scan-method pin

  7. a6a32b08

    Gate-tail round 4: displayTypeId staging across integration fixtures, airnaval async type table, source-pin reconciliation (track/naval/warzone/planxrifle/herobridge), wake anchor re-measure, running-gear refresh, naval/deployment promotion pin refresh

  8. b276ce8c

    Gate-tail round 5: airnaval report regeneration both tiers, humansurface/paintlayer texture-only vram pins, combatground camera stub, shroudvisual manual boot, terrainmacrovisual runner url passthrough, motiongate.wip excluded as superseded

60 commits
  1. d041b48b

    Gate-tail round 6: stale-socket diagnostic values, humanunits manual-boot pin documented

  2. d0e47a6c

    Multiplayer plan of record: player-hosted architecture, issue register, task-level roadmap

  3. 572c674f

    Multiplayer plan: the standalone node stays installable; add community-server mode

  4. a2affd4c

    Multiplayer plan: one source of truth for the node; local VM lab; linux-arm64

  5. 4d212daf

    Multiplayer plan: VM lab uses free software only; Lima 2.x commands corrected

  6. 2a617cec

    Open-issues plan execution: InputHud civilian-notice tone + displayType warn; GateTools acceptance freshness-validate; VisualGpu cliff jitter, mushroom roof floor (2.0 gate-proven), water SSR dead-path bindings, skin residue fold; AudioWiring under-fire shout; stale-socket diagnostic detail

  7. 03242ace

    humanunits boot: fallback human body skips roster socket binding (no authored sockets for its source); recomposed candidate

  8. 0b4ccdf2

    Engine completion: land Cargo.ReservedCount, Session.Client.PingMs, FallsToEarth.PresentationParentId; observer computes presentation muzzle/shot from public API; provenance lock refreshed; rolepacksgate current-surface readback + e7 riki-meshy pin + direct vite spawn

  9. 03eda45e

    Production names complete: host prepopulates snapshot string table at first emit (all ruleset actors), client refetches stale/empty tables (holes + bounded empty retries, reset per world) and rebuilds production tiles once ids resolve; runs of gamecontrol/cargobutton/hudcontrol/e2faction green

  10. 9f9ee3fc

    Remove superseded motiongate.wip diagnostic (its own header documents the zero-diff A/B limitation; live successor motiongate.mjs)

  11. a7020d6d

    Owner decisions 2026-09-19: emissivegate per-class bands bracket the approved glow (47x/48x/8.4x, product untouched); nightlightgate records the approved night as committed reference with --record flow; vehicleproportion/humanstudy/deploymentpackage gates owner-disabled from inventory (studies approved, re-enable on model changes)

  12. 7bbaa74d

    Emit-time string-table prepopulation (rules ready by first snapshot); actormask known-red diagnosis (staged scene frames empty, mask untested); meadowgate known-red diagnosis (scan-window population instability); MP 2-bot smoke findings: lobby side proven live (local + 2 enemy bot clients, relation Enemy) but direct startSkirmish path leaves bots idle · real-UI collapse recheck queued

  13. 91fe27d7

    Fix HUD flicker regression: in-range empty type-table holes are permanent (host lazily registers nameless entries post-prepopulation) · the holes refetch round-tripped the worker every actorTypeName call and hitched the frame; keep out-of-range and bounded empty-table refetches only

  14. 7fd3c645

    Restore the health bar / selection ring snapshot-position fallback (9002b17): the intermittent mesh-capture miss (bucket rebuild, capacity squeeze) blanked the selected-actor overlays every other frame · the flicker the owner reported is this fallback lost in a merge

  15. e8fcf375

    Death voice bank generated on ELEVENLABS3 (gen-death-voices prefers the fresh key; 76 death clips over the full per-country/per-class/per-persona bank); voice bank embedded in the composed candidate, audionodegate PASS

  16. 75d994a0

    Owner decision option A: disable actormaskgate + meadowgate from the inventory (known fixture-level red, cosmetic risk only); DISABLED-GATES.md documents all five disabled gates with reasons, manual runs and re-enable conditions

  17. 904bde18

    ui: hide the multiplayer hosting panel in the Skirmish tab · main's hosting CSS set display:grid on #session-mp-panel which beats the UA [hidden] rule, so the hosting columns leaked into Skirmish after the merge; added the same [hidden] override #boot already uses

  18. ad3269e1

    landing: move the spec table and stack section from the home page into the story's 'The stack' chapter; nav 'How I built it' now links to /story, footer 'The stack' to /story#the-stack; graphics and showroom stay on the home page

  19. f7f2053f

    landing: restore nav.html · a multi-file edit batch truncated it to zero bytes, which emptied the site header on every page; restored from HEAD with the updated /story and /story#the-stack links

  20. d6cd10dd

    landing: restore the Redline Wars nav logo (redline-frame Redline + Wars) from the desktop shell wordmark, replacing the placeholder Steelthorn wordmark the truncated-nav restore brought back

  21. 45b02976

    landing: nav Arsenal links to /field-guide · the full asset catalogue page (all models with the three.js viewer) instead of the single-model showroom section on the home

  22. 22792d63

    landing: move the whole 'How I built it' section (crew, pipeline diagram, the nine acts) from the home page into /story as its closing section; home now flows hero → commanders → arsenal showroom → final CTA; redundant read-the-story button removed

  23. 4d10ed5a

    landing: navbar 'How we built it' renamed to 'How I built it' and links to /story; story section eyebrow matches

  24. 39fa9f56

    landing: blend the How-I-built-it pieces into the story's reading order · crew cards open the crew chapter, the pipeline diagram closes Act 3 (the sync contract), the nine acts overview opens the story before Amsterdam; the emptied bottom shell is hidden

  25. 8d0e4f96

    landing/story: 'Steelthorn was not a one-shot prompt' renamed to 'Redline Wars was not a one-shot prompt'; localStorage keys (steelthorn-nickname/-sound) and STEELTHORN_* server env vars intentionally kept · renaming those would orphan stored player data and break the server contract

  26. 68224eb3

    story: five-day update · Act 6 carries the player-hosted architecture of record (desktop hosts, owner runs the relay, hardened spine, live two-bot proof); new Act 10 tells the gate marathon (201 commands, 54 reconciled failures, engine completion, freshness-validating entrypoints, owner-approved references); numbers refreshed (570+ commits, 328 banked clips, gate inventory row); the relocated built sections styled to fit the story page

  27. aa4e802a

    landing: the arsenal section becomes a light showcase (new title, no tech lead, no numbers strip) · the stats and the loaded-live explainer move to the story's numbers chapter (models in a live match 277, 1,368,266 triangles at full detail, joints 1,503); leaderboard preview removed from the story page (it has its own page)

  28. ef442798

    landing: hero cast thumbnails (Gen. Young, Gen. Kirkov, Riki, Jackson) top-right with names, smooth-scrolling to their dossiers; stats bar becomes Free to play / Maps 67 / Leaderboard; arsenal showcase title softened, tech lead and numbers strip moved to the story

  29. 82b6b512

    landing: v0.4 alpha badges on the nav wordmark and the hero title

  30. 2b868b08

    landing: hero cast thumbnails restyled to blend into the hero (rounded, gradient fade with overlaid names, hover zoom + red tint); alpha badge reads 'Alpha v0.4' with the version smaller

  31. e0548a86

    landing hero: the alpha badge floats above the word WARS, right-aligned to the word's end (out of flow so the title stays on one line); v0.4 slightly smaller

  32. ff7e94e8

    landing: alpha badge floats above the word Wars on both the nav wordmark and the hero title (positioned wrapper), black background with a thin light border on both; nav badge styles moved to landing.css so every page renders them

  33. 9d098b09

    deploy: single-VPS v1 hosting scripts (caddy, systemd units, provision) for one-origin browser play

  34. e0885e64

    ordergate: unity-port order-path gate script (own sim host, SSNP stream + RPC bridge)

  35. 43bca008

    gitignore: standalone engine builds, local scratch/output, tool state, unity backup tree

  36. ba03f3f7

    launch-dedicated: invoke the SERVER_RUNNER function directly in the legacy restart loop (set -u crash made every non-room-mode dedicated exit 1)

  37. f8c0d443

    mp node foundation (T1.0 partial): protocol.json constants, node-manifest, assemble-node assembler, node-cli entry point, nodeparitygate, web mp-protocol module; roomhost wiring + packager cutover next

  38. f4820bf4

    phase 0 truth: §1 rewritten from measured gates (twoclient/shroud/spine/federation/desync PASS, lan-ui FAIL on the real-UI host flow, fed-1 FIXED and closed, boot 0.6s, T0.9/T0.10 recorded), new defect A21 documented

  39. f5015249

    wip (mid-fleet): ui marks de-gating re-applied from 5f3f0e8 (flicker fix, verified 300/300 frames visible with selected MCV) · this commit also captures in-progress UiFlow T1.14/T1.16 bridge+join work that does not compile yet (MP_STRINGS/MpJoinRefused pending); the next ui commit completes it

  40. 14ec724e

    Merge experiment/lod-cache into main: the steelseed-gpu perf wave lands

  41. 3ce8fe6d

    relay (T1.20-T1.25): ip-keyed client identity + rate prune, ws ping/pong liveness with relay-side RTT (JSON pings removed), L11 caps (token bucket 4008, socket/channel caps, register window 4006), room ownership + whitelisted clamped summaries, pending-create rejection + chanId wrap, --data-dir stats path; constants from protocol.json

  42. 3f02f4ed

    transport (T1.11-T1.13): shim fails closed (per-item try/finally, create-failure + connect-timeout + dead-send close info, recvCap slicing), C# pin lifetime on served WorkClose + dead-id no-op + in-place partial sends, sensitivity-tested unit suite 6/6

  43. 7377f63a

    client (T1.14-T1.19, T1.31): async checked join flow per §5.10 (fresh probes, epoch, close-code strings via mp-protocol), lobby rule L8 with admin Start match + LobbyCloseSlotsDownTo + Leave, keyed room list with scoped polling and joinable-only rows, desync/disconnect banners, bridge probe/leave/notReady/closeInfo methods, C# exports (LeaveMultiplayer, epoch, password, NotReady, map check, DevOverrides guard), multiplayer match reports refused 409 unranked; completes the wip ef08603

  44. d933bf1b

    structures: sink the plinth and apron below grade · no more square side panels

  45. daf33803

    node (T1.0 wiring, T1.1-T1.7, T1.9, T1.10): roomhost exports startNode/parseArgv with node-cli entry; direct runner spawn (no shell, pinned §5.4 args, per-room support dir, stdout drain); stdout state machine with single-use rooms + claim TTL; one counted player path + real idle-kill floor; pidfile/orphan sweep/tree-kill; port probing; loopback-only API with Host/content-type/key guards + CORS + rate bucket; map-catalog validation; packagers cut over to assembleNode

  46. 82c31716

    gates (T1.26-T1.30): nodegate (11 guards, GPU-free), mpgates runner with per-gate timeouts, gate-map.json pin (T1.27), mp-twoclient/mp-shroudgate moved onto the keyed roomhost API with SetWsEndpoint joins (legacy relay retired from the normative set), mp-relaygate (3 clients, manual L8 lobby, 60s idle-kill, pairwise sync hashes) and mp-lifecyclegate (4 lifecycle cases) authored for the run phase

  47. 4ffb2818

    audio: the ElevenLabs death bank never loaded · sfx glob skips mp3

  48. 819e5178

    audio: picking Soviet now speaks Russian · voice and sfx banks routed

  49. 5bd7d005

    audio: unit shouts get a voice budget · drop, never queue TTS

  50. 007c65ef

    audio: port the rules-driven death voice wiring from the gpu branch

  51. 13fa8073

    gates+node: keyed-API boot for lan/desync gates, L8 lobby flow in lan-ui, lifecycle case-d all-leave + pidfile proof, evaluate one-arg joins in twoclient/shroud; §5.9 __redlineLocalNode hook lands early so the real-UI host POST can carry the node key; relay registry keeps reserved rooms routable while the public directory stays lobby/playing (node reports state)

  52. 72a75bc8

    audio: voice glob loads mp3 clips too

  53. 4627ad61

    input/tablet: pointer events for iPad and Android · desktop untouched

  54. 4dac5689

    engine/audio: emit the death voiceKind byte · activates the death voices

  55. 2af45e72

    engine/audio: the observer and emitter hunks of the voiceKind port

  56. 1871b06f

    client+transport: wire the never-called 1 Hz lobby/match tick (drove S13 handover, Start-match enablement and S15/S16 banners · fixes relay gate and lifecycle a+b), coded native closes now replace the synthetic send-failure record so the real 1011 close surfaces as S16 'host went offline', lifecycle case a completes the L8 ready-up choreography; browser suite green: twoclient 203/203, shroud 37/37, desync, lan, relay, lifecycle 4/4

  57. 98b95458

    sim build id (T2.1): treeHash/computeSimBuild per §5.8, build-ra-mod stamps mod.yaml Version + writes generated/build.json (simBuild cdcba1e589ee, modHash), compose copies AppBundle build.json with app version and fails loudly without it

  58. 4bd690cb

    node /v2 (T2.2-T2.5): §5.3 API (health, immediate 201 create with booting state, keyed DELETE, standing-mode error), local wsUrl, protocol-2 registration (register/registered, backoff jitter, 4003 update-required stdout + exit 3, 4004 retry, duplicate open close), build.json verification at start with REDLINE_GATE=1-gated skip, node-key-file + mode/name flags; nodegate 13/13

  59. 26e43422

    relay /v2 (T2.2, T2.5-T2.6, T2.3): §5.2 API (config, authoritative wsUrl rooms, placement-off ladder, legacy 410 POST), open registration protocol 2 (nodeId from nodeKey, 4002/4003/4004/4006, protocol 1 owner-token-only), SIGHUP config reload, SIGTERM drain, strict Origin-echo CORS, bundle behind --serve-bundle; relay-smoke + relay-registration tests 14/14

  60. ccbd51d6

    client /v2 (T2.3-T2.4, T2.7-T2.8): resolveRoomUrl host-match rule replaces roomEndpoint, S20/S21 reserved polling on /v2, launch-args allowlist (Player.Name only off loopback) wired into both boot paths + csproj, room browser on /v2/rooms with typed row guards; tsc clean

39 commits
  1. 610b676b

    transport+units: per-call rpc timeouts (fail-closed, no wedged join state machine) and cooperative yield in buildDamageLadders · the 12 s synchronous quadric LOD build starved the page event loop, freezing the §5.10 join flow past the S11 window; version gate 3/3 (S11 within 10 s) and lifecycle 4/4 green

  2. cdda182d

    T2.8 cutover: consumers move to /v2 and the legacy contract dies · node /rooms routes, bare-path upgrade fallback and wsPort/wsPath/port summaries removed (relay keeps its read-only legacy GET /rooms + 410 POST), ws-relay directory deleted and every remaining gate/probe joins via keyed API + SetWsEndpoint(wsUrl), desktop tile/selftest on /v2, §9.1 doc rows synced; full suite re-verified 7/7 PASS post-cutover (twoclient, shroud, desync, lan, lifecycle, relay, version)

  3. ed5bc1b0

    node: track dedicated-runner.mjs (missed from the T1.1 commit; roomhost imports resolveRunner/buildArgs/killTree from it)

  4. 7e8b14fa

    packaging (T3.12-T3.14): per-RID standalone publish (all five RIDs, self-contained + mod dlls + build.json), one packaging entry with loud simBuild/map-catalog preflight and per-RID assembleNode staging (mac dir targets built end-to-end, win nsis + linux AppImage structural), NSIS LAN firewall rule, thin node package wrappers (byte-exact start scripts); A14 helpers deleted

  5. aac0bf17

    ui host flow (T3.1, T3.3, T3.7, T3.8): three-way mpHost router (shell hostStart dialog S19 → keyed create; harness hook path byte-identical; browser shows S2), S19 dialog with lan/public visibility + S22 relay-gated 'Anyone online', S18 address reveal with private/.local validation via shell lanQuery, server.mjs --host defaults loopback; play-mp migrated to the keyed harness

  6. 3413fd48

    desktop shell (T3.2, T3.4, T3.6-T3.7 shell, T3.9-T3.11, T3.13.6): node on demand with 15s health wait + 30s idle auto-stop, public hosting without shared secret (spine tunnel smoke-proven), LAN listener child + lanQuery, quit guard S17 + powerSaveBlocker, update-required gate S14 with download-host whitelist, window hardening (sandbox, will-navigate allowlist, deny popups, set-mp-dir validation), linux WebGPU switches

  7. 3c5b120c

    node LAN + standing mode (T3.5, T3.6, T3.16, T3.19): UDP beacon announcer/listener per §5.7 (source-derived endpoints, spoof/oversize/foreign-app drops), discoverygate 7/7 multicast+loopback, standing community mode (rooms-file boot, reserved-skip, 5s replacement, 5-crash guard, 403 standing-mode, --lan rejected), nodegate 24/24 incl. standing assertions; manifest + protocol constants extended

  8. a7e739dc

    input/tablet: hide the camera pad on fine pointers + ?seed= determinism

  9. 97a0b6ec

    tools: playtest fails fast on the missing legacy skirmish seam

  10. fa4e64c4

    desktop: pin the local node to --mode own explicitly (NodeLan's node-cli default flipped to standing for community servers; the app must never become a room source)

  11. 7e11973b

    gitignore: stop tracking bin-standalone binaries · T3.12 publishes them outside packaging per RID; the standalone node zip/npm path (T3.20) assembles them fresh

  12. 727ac8c9

    readme: multiplayer status updated after Phases 1-3 (all gates green, host-flow break fixed, packaged selftest/CI/per-OS verification remain)

  13. 141a753e

    selftest (T3.21, T3.15): GPU-less SELFTEST_GPU=0 path (bridge-based boot, solo room via keyed IPC, orphan assertion) passing in dev tree and on the packaged mac-arm64 app (netframe 52, outofsync False, gpu:false); fix node-cli buildOptions dropping --http so the desktop health poll reaches the node API (nodegate +regression check, 25/25); packaged Resources/server.mjs shipped

  14. 7385ef07

    CI + community zip (T3.17, T3.20): steelseed-mp workflow (3-OS matrix, unit+nodegate+discoverygate, zip job building and gating redline-node-linux-x64), pack-node.mjs per-RID zip with loud preflights, unit file per T3.20, DEPLOY.md community-server requirements; zip boot-proven locally (standing room lobby in 2s, nodegate 25/25 from inside the zip, no tokens shipped)

  15. 81825390

    engine: track package-lock.json (npm ci in CI needs it; ws is the node's only dependency)

  16. 4c9b8ace

    sim build id excludes obj/bin build products (T2.1 acceptance: the id drifted between publishes because generated AssemblyAttributes under obj/ were hashed); compose ships net-config.json per T4.3 (web/public copy wins · the switch leaves off only deliberately)

  17. f4391c91

    audio: shouts never outlive the soldiers · clip cap and dead-shooter skip

  18. fdad151f

    deploy (T4.1/T4.2 prep): relay-only provision script (Caddy, Node 22, steelthorn user, /v2 relay.json with acceptedBuilds 2a4343e7fae7, systemd §10.2, ufw 80/443) and command.md with the exact Mac/VPS command sequence

  19. cfe51e47

    relay-deploy fixes + T5.1 net-config loader

  20. 3d62ac8f

    merge codex/steelseed-completion into main-line export for nillo/redline-wars

  21. 8a7b63b8

    deploy pipeline + T5.2 switch + download page (export to nillo/redline-wars)

  22. 311c309d

    ci: build OpenRA.Mods.Cnc · ra's --map-hash needs its DLL (fixes fresh-runner failures; same defect exists in the monorepo steelseed-mp.yml)

  23. 01eb1b86

    deploy: AppBundle ships from the dev Mac (L19) · CI builds landing+node+relay only

  24. c340e9ae

    deploy: red-gate override for the bundle ship + node install mkdir fix

  25. f51478d3

    cutoutgate: port fixtures to the quantized vertex layout (f16x4 positions)

  26. 22b8349f

    deploy: extract build.json from the payload tar (stage no longer carries a loose copy)

  27. cdc15420

    deploy: correct payload paths on the server; relay provisioner respects the full Caddyfile

  28. d4921e66

    provision-relay: gpg --batch --yes (re-provision over SSH has no tty for the keyring overwrite prompt)

  29. f18dcf08

    provision-relay: ufw --force enable (yes| pipe SIGPIPE 141 under pipefail)

  30. 5466a3df

    deploy: restart the spine on provision (enable --now leaves the old process, stale acceptedBuilds); smoke retries runner transients

  31. b30e4a69

    bundle ship: dotnet publish (release-aot) + SnapshotEmitter.StructureBuilt + node drop-in [Service] header

  32. 22c012e6

    E2E round: engine variant fix for the browser bundle + net-config relay semantics

  33. 219cd0a1

    split deploys + landing Play link + skirmish maps in the wasm VFS

  34. 1f0d8a85

    revert the wasm VFS maps experiment: browser MapFolders need enumeration the wasm FS does not implement (boot-fatal); browser skirmish maps remain a plan gap (server-side map transfer)

  35. 8c0b3291

    restore wwwroot/sim-worker.js (lost to an rsync --delete recorded in the E2E-round commit); coherent rule-8 pairing verified live: boot clean, no bridge dispatch errors, hosted room list served

  36. 2abb9753

    grid status page + Play links + a-path-beyond excluded + maps package mount

  37. d3c85c48

    ship the interop sides host main.js imports (openra-audio/agent-mode/fs/gl/input)

  38. a196e45d

    spine.redlinewars.online node endpoint + grid status dot + landing polish + story update

  39. 16f062f0

    INFRASTRUCTURE.md: the whole topology for the next agent (no keys)

21 SEP – 27 SEP · 288 COMMITS

The hard finish

The busiest week of the project, and its heaviest day. The picture pass, the second soundtrack, the war-room console, four more songs, vfx closed out, and every voice bank re-rendered on one provider.

33 commits
  1. eb5e58ab

    fix browser pump, multiplayer routing, and release pipeline

  2. 2d1f9232

    fix multiplayer fixtures on Windows CI

  3. 491d3aa0

    make standing node gate deterministic on CI

  4. 570b093d

    terminate Windows host process trees cleanly

  5. efae7f97

    prevent completed Windows tests from retaining CI

  6. 63f770c4

    order Windows fixture cleanup after process shutdown

  7. cdd4f07d

    publish release downloads from the public site

  8. 57179c7a

    stabilize cold matches and active lobbies

  9. 86560ff3

    hide homepage account controls

  10. ee3105bc

    restore browser contextual order bridge

  11. 90d50321

    restore building placement and remove false scorch plates

  12. 61b01d81

    clarify play modes and curate arsenal

  13. ff82382b

    gate multiplayer rollout behind debug flag

  14. 410e7487

    hide multiplayer identity while rollout is off

  15. 16261bb4

    default game graphics to dynamic and specify ranked identity

  16. 4e0d598c

    specify ranked data plane and frontend completion

  17. b1f9915f

    complete secure multiplayer architecture and release gates

  18. ae4aacf7

    build landing before built-route CI tests

  19. ba8913ea

    make browser source typecheck independent of forge payload

  20. 3ae11aed

    make desktop source gates AppBundle-aware

  21. 296f010c

    harden production multiplayer deployment

  22. fcdf8bea

    fix Windows ranked replay test gate

  23. 36f19cb7

    document live multiplayer and SMTP deployment

  24. 8550877f

    automate shared game releases and default mountains off

  25. 43454f91

    fix clean CI forge baseline restore

  26. 714b5352

    fix Blender environment handoff in release job

  27. f5443949

    pin external authoring sources for clean releases

  28. 7e8a1671

    catalog tracked riki Blender LOD sources

  29. 0138bc84

    run voice coverage gate after web build

  30. 82b3e16e

    pin browser build to dotnet 8 sdk

  31. 449425d9

    run ci when dotnet sdk pin changes

  32. f4b5a101

    use canonical utility build path for browser catalog

  33. 980a1798

    build web assets before preview release gate

23 commits
  1. b1f90406

    retry transient headless WebGPU release readback

  2. 9b62baa6

    speak construction complete only for structures; units keep unit ready alone

  3. 9ee61074

    run renderer release gates on macos webgpu (L19); linux swiftshader dies at readback

  4. 82ca58d5

    truth-sync: account API deployed, CI lanes green, dispatchable deploy components

  5. 70efe7a4

    gpu-gates: restore forge baseline before vite build

  6. 381a7fa5

    portable forge baseline restore for macos runners (bash 3.2, shasum); zstd guard

  7. 4c29f027

    desktop packaging: pin electron-builder projectDir so packaging works from any cwd

  8. 3ae65d12

    OPTIMIZATION: faster browser frames on large battles

  9. 33e6ed1c

    terrain: round material transitions off the cell grid

  10. 4d485174

    fix: expose ora while the simulation runs in the worker

  11. ee74ab74

    fix: serve the simulation page proxy on the play host

  12. 90262359

    story: the live week, the 60-frame close-out, and why Grok 4.7

  13. 1772d37c

    terrain: pull material edges off the one-metre stair

  14. 46916657

    fix: fit civilian houses inside the lots they replaced

  15. 3f33748c

    fix: smoke nearly-dead troops

  16. f89e5e77

    fix: stop the phone from reloading the game

  17. 73d0418f

    fix: let a phone finish loading past 45%

  18. 0bfa1cb2

    story: correct the crew model versions

  19. e40ce3da

    story: tell the build as one road, and draw the network and the factory

  20. 35e5a6a0

    story: name the M3 Pro and the maps the numbers came from

  21. 94b798db

    story: tell the full Redline Wars build with verified diagrams

  22. 6f0cc24b

    brand: apply official Redline Wars marks across site and app

  23. 7bcb29f0

    fix: sign packaged Mac multiplayer host and gate release

109 commits
  1. 2c15cacc

    brand: use white Redline letters and transparent R icons

  2. 3b418ef9

    brand: rack button lower edge on hover and simplify header

  3. d5c30764

    brand: slide the separate front button sled on hover

  4. 706f40cb

    brand: retract button sled nearly to rear hook

  5. 0e7f557a

    brand: close nav button seam through hover frames

  6. cce02513

    brand: align sliding button contour with logo

  7. 48e663ba

    dev: proxy root game modules through local landing server

  8. b92e16be

    test: await multiplayer sync probes and send relay origin

  9. 14bd4ba9

    ci: make releases manual and cap artifact retention [skip ci]

  10. f7c420da

    ci: add reproducible light API and relay release lanes [skip ci]

  11. 28064200

    ci: skip unrelated release artifacts for game and node [skip ci]

  12. ea90336f

    render: reject TAA history for forward skinned pixels [skip ci]

  13. b2652afd

    test: wait for node capacity in lifecycle scenarios [skip ci]

  14. ca34b465

    brand: use white-backed app icons and favicons [skip ci]

  15. c97632a1

    desktop: inset rounded transparent app icon [skip ci]

  16. f93e3ccd

    docs: record current multiplayer and release evidence [skip ci]

  17. cabbfeba

    multiplayer: complete browser join link flow [skip ci]

  18. 0301ea3b

    docs: record Linux x64 node VM proof [skip ci]

  19. 4437d024

    desktop: preserve signed node parity and bound selftest [skip ci]

  20. fd4279fb

    Inset macOS app icon within transparent canvas [skip ci]

  21. 0a73f060

    Avoid Keychain access on unsigned-in desktop startup [skip ci]

  22. 45d19883

    Record exact package and Linux community-node verification [skip ci]

  23. 5c582194

    Document measured Actions cost and component deploy lanes [skip ci]

  24. b7fd6f0a

    Record Rosetta x64 desktop hosting smoke [skip ci]

  25. 033c27c2

    Record native Ubuntu arm64 node and relay lab [skip ci]

  26. 6b0e591a

    Fix Linux arm64 AppImage runtime dependency [skip ci]

  27. fb1a8040

    Record emulated Linux x64 AppImage boot limit [skip ci]

  28. 81bb79ee

    Record x64-host arm64 AppImage runtime patch proof [skip ci]

  29. 8c587da8

    Add reproducible Linux multiplayer VM lab [skip ci]

  30. 020aea8f

    Scale nodegate observation deadlines in emulated VM [skip ci]

  31. ef10f062

    docs: record pinned Ubuntu x64 multiplayer lab result [skip ci]

  32. 5276fa18

    docs: record frame gate and measured Actions run costs [skip ci]

  33. 08c6cce6

    docs: record five-minute paired multiplayer sync [skip ci]

  34. 540bf666

    Verify Linux x64 AppImage multiplayer in Ubuntu lab [skip ci]

  35. de7001d0

    Guard packaged desktop resources across all platforms [skip ci]

  36. 04d96e84

    Record five-minute relay proof and prevent duplicate test joins [skip ci]

  37. 95082fd9

    Require exactly two remote relay channels [skip ci]

  38. 01ac6999

    Verify live game HTML matches released AppBundle [skip ci]

  39. aeef6f5c

    Verify deployed landing pages and brand assets by hash [skip ci]

  40. 78a74957

    Verify mirrored installer and node checksums [skip ci]

  41. 93b94840

    Document coordinated browser join rollout and relay gate proof [skip ci]

  42. cec2576a

    Record current AppBundle frame cadence gate [skip ci]

  43. 09332de8

    Pin native Lua architecture in every standalone runtime [skip ci]

  44. e3c57d4c

    Document component-scoped release dispatch and cost control [skip ci]

  45. 786bdedd

    Add verified artifact-only release promotion path [skip ci]

  46. 5ba49590

    Resolve candidate manifest path during artifact promotion [skip ci]

  47. 32a7da7b

    Keep active multiplayer tunnels through Caddy reloads [skip ci]

  48. 4054283e

    Release Ranked players when their match settles or is voided [skip ci]

  49. 6dbd547e

    Stop the local web server and LAN listener on every desktop quit [skip ci]

  50. f2d1644d

    Bind hosted dedicated servers to loopback only [skip ci]

  51. 241037d2

    Guard the actor-type table refresh and drop superseded reads [skip ci]

  52. d33055a4

    Tolerate one missed relay heartbeat before dropping a socket [skip ci]

  53. 84f4003f

    Keep ordinary rooms off the owner node unless ownerOverflow is set [skip ci]

  54. f4fbfff2

    Reapply the renderer's quality on a new world and hold the governor in the lobby [skip ci]

  55. 2d79a06e

    Never take donation consent from the renderer [skip ci]

  56. 40189c24

    Cap unregistered relay tunnels so they cannot lock real nodes out [skip ci]

  57. 636f9cd6

    Ask donors again for consent saved before the renderer boundary fix [skip ci]

  58. e9e39bc5

    Make desktop sign-in approve only a code typed from the player's own app [skip ci]

  59. 463f290c

    Release the governor only when the new world's first snapshot lands [skip ci]

  60. caff7779

    Keep a reconnected node's rooms and players; retire unaccepted builds on reload [skip ci]

  61. 3c1953eb

    Stop Dynamic promoting grass it never loaded [skip ci]

  62. 2b271f17

    Never publish the host machine's name as a room's hostName [skip ci]

  63. db326f27

    Apply the relay's node cap at register so a node can replace its own tunnel [skip ci]

  64. 3d533749

    Restore quality only with real headroom, after a cooldown [skip ci]

  65. 8e608e8c

    render: per-tier quality, stable TAA and restored soldier depth pass [skip ci]

  66. 8b42a7bd

    terrain: blend the four cells around each pixel [skip ci]

  67. 62183941

    fx: soft ground-mark rims; snow tracks on snow-covered hard ground [skip ci]

  68. 1b825131

    render: no weather snow on skinned actors [skip ci]

  69. 3b99079d

    ui: health bars only for visible units and buildings [skip ci]

  70. 31cc4096

    audio: rotate a second soundtrack during matches [skip ci]

  71. fe51a9bd

    build: compose --prune drops earlier builds' assets [skip ci]

  72. 20140058

    node: rotate idle tunnels before Caddy's cutoff and drain without restarts [skip ci]

  73. d9144825

    node: dedicated servers get an allowlisted environment [skip ci]

  74. 6e27dfd6

    deploy: validate the node artifact first and roll back a failed install [skip ci]

  75. 8e36c456

    desktop: quit when the menu closes before a game is shown [skip ci]

  76. 489235b5

    gates: a missing multiplayer gate fails; treeboot checks response.ok() [skip ci]

  77. 9c7337f9

    docs: record the published candidate and today's multiplayer evidence [skip ci]

  78. 6c30f73f

    render: screen-space contact off in every tier by default [skip ci]

  79. fd286cd2

    leaderboard: say whether a skirmish counted; keep undelivered reports [skip ci]

  80. 9c615f8b

    desktop: let finished-match reports through the account broker [skip ci]

  81. f5510465

    tablet: a tap selects, and the lobby fits a touch screen [skip ci]

  82. a2433734

    accounts: sign-in and registration are public; multiplayer stays gated [skip ci]

  83. 63e0021a

    story: what a faster frame cost, with the picture pass and new numbers [skip ci]

  84. 73f3dfe3

    Merge branch 'fix/review-20260923' into release/20260923

  85. 84f286e9

    boot: yields a hidden tab cannot clamp, and a stage timeline [skip ci]

  86. 6df9cff1

    mp gate: watch the status line a join actually writes [skip ci]

  87. 2d6df033

    Merge branch 'perf/browser-frame-optimization' into release/20260923

  88. fcbe4345

    lobby: a room-list Join never tears down the seat you hold [skip ci]

  89. 3e1fd847

    gates: a signed-out account answer on loopback, and two harness fixes [skip ci]

  90. 8bd0a663

    Merge branch 'perf/browser-frame-optimization' into release/20260923

  91. d38ea301

    forge: pin baseline v2, which adds the second soundtrack [skip ci]

  92. 0aef8972

    ranked: pin sim build 9dc9b0e8d871 from a real E2E [skip ci]

  93. 33c75936

    units: the Soviet grenadier wears e2's body, not a civilian's [skip ci]

  94. 11d1e3cd

    quality: Ultra shows falling rain and snow [skip ci]

  95. b2c846fb

    wrecks: a destroyed building collapses and is gone [skip ci]

  96. 773c3817

    lobby: time limits read as minutes; bot personality only for bots [skip ci]

  97. 704c4195

    gates: the Soviet grenadier's body and Ultra weather, in the live runtime [skip ci]

  98. e4f97289

    render: rain and snow stay visible under the stable TAA [skip ci]

  99. 77f2e585

    deploy: a game-only release creates stage/ before archiving [skip ci]

  100. 91cb160e

    render: rain and snow mark TAA in the depth prepass, not a pass of their own [skip ci]

  101. c9c28c90

    multiplayer: public browser multiplayer is on (full), hosts may overflow to the owner node [skip ci]

  102. 80ed1bb6

    game: phones get a desktop-or-tablet note instead of the battlefield [skip ci]

  103. d3abd88d

    deploy: contract test titles say what they now check [skip ci]

  104. 4d9d0895

    landing: the snowy base by day and night, close-ups gone, cast clickable [skip ci]

  105. aa3c0490

    landing: the /play room browser stays closed while the game plays online [skip ci]

  106. 5c1125ac

    landing: on phones the logo leads the header, PLAY FREE is an outline [skip ci]

  107. 8b9c09e7

    landing: every play button fires a rifle shot before the game opens [skip ci]

  108. 51b46b24

    web: local dev and preview answer with a local net-config [skip ci]

24 commits
  1. 2a5b0a5c

    deploy: the API job no longer races the relay over the public switch [skip ci]

  2. cdb38e2d

    gates: composedgate clicks the drawn MCV after the camera has fully settled [skip ci]

  3. 8116fb05

    edge: the play CSP allows the phone gate's inline script [skip ci]

  4. 2fd7ec45

    edge: list the phone gate's CSP hash once [skip ci]

  5. 5f60a32b

    deploy: validate the edge Caddyfile with the caddyfile adapter [skip ci]

  6. 52202164

    multiplayer: a hosted room seats at most the selected map's players [skip ci]

  7. b15f5181

    units: a shot-down aircraft crashes flat and burns out instead of hanging

  8. 3d2643fa

    landing: the community-server card gives the node's real start command

  9. 1ad32e5e

    landing: the hero stats bar keeps every value inside its own cell

  10. d26ede1a

    landing: single-column sections fit a 320px phone

  11. bcdbe823

    deploy: fast lane caches for unchanged engine and art inputs

  12. 9c70524e

    deploy: forge export cache key includes the baseline pin

  13. f60eaa3f

    civilians: sixteen colour variants of one shared body

  14. 87cf8667

    node: zero-config start for community nodes

  15. 561717ec

    desktop: Electron 44 and electron-builder 26

  16. 3c22b9f1

    units: procedural base meshes feed the LOD cache digest

  17. 5f60f3db

    desktop: ship staged node npm modules as filtered extraResources entries

  18. 4e00f0d0

    node: bundle the Node runtime in the Windows community zip

  19. 5164e2d2

    landing: keep public downloads hidden behind their own switch

  20. 9d7abf01

    weather: snow falls again · per-frame budget, readable flakes

  21. 61ca2bfc

    snow: settled snow buries the meadow; flakes sized with the camera

  22. d19f3d9e

    lod: repair shading normals the decimator turns away from their faces

  23. b6458c13

    desktop: a node refusal no longer claims "download the new version"

  24. aa9ab696

    desktop: the "Update vereist" modal no longer covers every launch

59 commits
  1. d08b7a87

    setup: hidden forms stay hidden, rooms show map titles, S23, readable default colours

  2. 07488dae

    web: build the brand's two OFL faces into the bundle

  3. d3e4d23a

    ci: the forge cache key names exactly what the Blender export reads

  4. f915ecf8

    setup: the war-room console · loader, skirmish setup, settings, account, UI sound

  5. 2c1c6fa7

    Setup console: multiplayer as its own destination

  6. 942d3cff

    brand: Jolyn Studios logo and Redline Wars press copies, one press-kit zip

  7. 34edb360

    landing: Jolyn Studios logo and the press kit in the footer

  8. 8f45ec90

    setup: desktop main menu and console host dialog; the map name reads as a picker; the logo goes home

  9. a7f4120d

    camera: Alt/Option + drag orbits and tilts, for trackpads without a middle button

  10. f7540f8a

    desktop: intro and the war-room console landing

  11. 162aa2e3

    audio: Calm Before the Battle · the loading-screen and menu track, and song three in battle

  12. 10fd638c

    audio: Cold Start, Still Standing and Reverse Order; battles shuffle all five tracks

  13. 8742f5fd

    oss: export script and public README for github.com/jolynstudios/redlinewars (prepared, not published)

  14. 3eff14ca

    relay: nothing a node or a request sends can crash it; join mode never hosts; debug bypass off

  15. c178ce9a

    multiplayer: browsers join, only the desktop app hosts; the account names you

  16. 9e9ebdf8

    setup: no debug menu switch; the option stays at its default, off

  17. bff85950

    deploy: a relay-only release pins to the live build; CI runs the relay hardening tests

  18. 23240e60

    desktop: alpha notice with a bug-report link; Classic is the default graphics preset

  19. 8a216ddf

    hud: first-match tutorial with the manifest's build order; ready tray; red rings on every enemy; alpha notice; Classic by default

  20. 2d8cfbfd

    quality: Classic on a strong GPU, Dynamic everywhere else

  21. 5a110abd

    hud: game menu and match result in the console style; Restart match; Main menu on the result; rifle shot on Start and Join

  22. 556a129e

    audio: the menu song plays from the loading bar on, into the menus without a restart

  23. 57d51d76

    rules: the Radar Dome reveals 16 cells powered (8 on low power)

  24. 05c5e67d

    release: simBuild a53a1d2ded95 with the Radar Dome's 16-cell reveal

  25. 0a07e05e

    brand: the spark j is the J of JOLYN · no more double J

  26. 9a76917f

    test: skip the relay's SIGHUP retire-on-reload test on Windows

  27. 9dbf8a51

    engine: radar-pinging support powers and nuke detonations no longer halt the sim

  28. fa7dc946

    gates: pickinggate, targetfeedbackgate and hudgate follow the current input and picking contracts

  29. e5d7cde3

    render: opt-in GPU pass timing behind ?gputime=1

  30. bd45c69f

    tools: vfxbaselinegate measures the complete running game at Ultra and Ultra+

  31. 93b73394

    engine+web: impact events reach the presentation again, and no event is lost between frames

  32. 42fd4e86

    gates: refresh the weapon-visual source fingerprint the Radar Dome release left stale

  33. c074aec1

    input+support powers: clicks, Guard and superweapons do what the screen says

  34. 9efdcca0

    tools: vfxbaselinegate stops a window at game over or a stalled sim, and orders by sim tick

  35. fb088346

    gates: contextordergate reproduces the two click reports with fog on

  36. ffb6045f

    gates: spyoutagegate proves Spy -> POWR and APWR outages end to end (vfx.md S01)

  37. ec19c6a8

    gates: guardgate proves G arms the next click instead of acting at once (vfx.md Epic 2)

  38. 669e58eb

    tools: vfxbaselinegate times every GPU frame of a window, not only the last 600

  39. cd547fc1

    tools: vfxbaselinegate --profile samples the main thread of chosen windows

  40. f548059f

    docs: VFX baseline at Ultra and Ultra+ on the M3 Pro, 1920x1080 (vfx.md Epic 0)

  41. 8f2e94f1

    perf: frame code reads the canvas size from the app's ResizeObserver, not the DOM

  42. 73e7d0f7

    gates: uigate stubs location, which the multiplayer switch reads after its checks

  43. 6189279b

    docs: main-thread profile of an Ultra battle before the layout-read fix

  44. b9c9f2c3

    perf: frame code reads the canvas size from the app's ResizeObserver, not the DOM

  45. 6966328e

    gates: uigate stubs location, which the multiplayer switch reads after its checks

  46. 52163f4e

    merge: main (the web-only FPS release 6966328) into vfx/ultra

  47. b10733a7

    gates: ruleparitygate follows build-ra-mod's licensing exclusion of a-path-beyond

  48. 8d378c16

    ranked: pin simBuild f427f8d8c0f5 from the real two-client ranked E2E

  49. 40180f91

    bridge: the type table names every weapon before the first snapshot, so a first nuke draws its cloud

  50. 81263023

    gates: supportpoweruigate drives the superweapons and Guard through the real UI in a real match

  51. 00aee488

    census: vfx.md Epic 1 from the rules the shipping engine resolves

  52. 394a0afd

    fx: the cannon slice (vfx.md M4) · shells on their real flight, weapon-sized impacts, Ultra layers

  53. c76abe7e

    units: a muzzle per barrel from the rules, and an Ultra turret kick on firing

  54. 51324432

    tools: live-match harness, vfxscenario, combatvfx tests (vfx.md Epic 9, M4)

  55. 4062f305

    fx: the Tesla discharge gets its Ultra layers; a garrison fires from the rules muzzle

  56. ea27c950

    fx+engine: a heal and a repair show where OpenRA lands them

  57. aa98577a

    tools: vfxscenario covers the roster (vfx.md Epic 9, S05); after-fix baselines

17 commits
  1. 2f32a8fe

    engine: the nuke missile's flight, unit states, a true disabled flag, no enemy economy

  2. e8b95090

    fx: the staged Atomic and MiniNuke, support-power states, Ultra budgets (vfx.md Epics 7, 8)

  3. 5d4ddc96

    web: support powers read true on the units; scorched ground burns its grass

  4. b341512a

    units: an inoperative building's dish and rotors hold still again

  5. d60137bc

    docs+tools: VFX scenarios S01-S14, first use, rapid destruction, 60 FPS qualification, report

  6. d43b7fab

    ranked: pin simBuild c947fe0886c6 from the real two-client ranked E2E

  7. a1f6f0e1

    ui: the browser's first account check waits for the account host

  8. 666edbe6

    vfx: finish vfx.md · truthful support powers, spy intel, Ultra+ shimmer, materials, full in-game evidence

  9. 63e58b62

    maps: the pinned OpenRA tree drops its one Westwood-credited map

  10. 4c6da147

    ranked: pin simBuild fd9882019bca from the real two-client ranked E2E

  11. a87bf8d2

    fx: every nuclear detonation is drawn; S13 runs live

  12. bcb3c11e

    vfx: presentation measured on the real display

  13. 69fba625

    tests: the CI-workflow check skips where the workflow is withheld (the public export)

23 commits
  1. 9340f926

    joa: companion polish · every mode on mobile, taunts, landing section

  2. c43da7ec

    joa: render the five-language introduction with ElevenLabs

  3. f3241590

    landing: move the JOA section above the commanders block

  4. 325311d3

    landing: play the JOA introduction when the section scrolls into view

  5. 892c860b

    joa: the introduction speaks with the announcer's own voice

  6. f16aefe0

    joa: pairing dialog · approve pulses for attention, closes on connect

  7. 583e87b0

    Show attacked buildings and battle results on JOA; one pairing follows many battles

  8. 9cacda65

    Pair JOA from the settings menu, before any battle exists

  9. 514f2a76

    Move the settings-sheet Companion section up under Graphics

  10. 98720afd

    Boot: SHA-256 without SubtleCrypto on plain-http LAN previews

  11. 1e90653f

    Lead the settings sheet with the JOA companion feature card

  12. fe429006

    Companion control fixes: armed group attack, aircraft attack, repair and stop release

  13. 63bcd184

    HUD: locked build reasons, per-domain production wording, support and companion voices, fullscreen start

  14. 40e46635

    Voices: re-render the voice bank rows with ElevenLabs and gate the coverage

  15. 0d3230ea

    Words: the JOA assistant on the landing and blog, ElevenLabs in the credits

  16. 3c1fec42

    Merge remote-tracking branch 'origin/main' into feat/joa-companion-polish

  17. 70300a3a

    Pin forge baseline v6: ElevenLabs banks, JOA lines, taunts and the Jackson persona

  18. 4e53faa7

    ranked: pin simBuild 7f17f216e95c from the real two-client ranked E2E

  19. 4ffafec2

    mp-socket test: match throwOn with the joa admission query stripped

  20. 3da30699

    relay: ship companion-service.mjs in the spine payload; taunts provenance

  21. e7cffcca

    joa: companion polish, nuke alarm and countdown, tutorial step, forge v7

  22. 8532e659

    joa: ask screen, production status, nuke countdown, readable units, live landing plate

28 SEP – 29 SEP · 25 COMMITS

The companion and the community release

JOA pairs on the public page, the credits name the voices, and the release ships community placement on standing nodes, spectator-admin rooms, and idle demotion.

18 commits
  1. f5aee48f

    joa: turn on the companion rollout (JOA_ENABLED + net-config)

  2. 839236c1

    relay: route /v2/companion/ws to the spine's websocket listener

  3. 4340b537

    companion: docked legend, audible alerts, low-power shout; credits: voices are ElevenLabs

  4. ab470138

    jackson: price 700 credits (owner repricing), pins from the real ranked E2E

  5. 96886bd4

    joa pairs on the public page; clean public urls; server module; lobby kick

  6. b1e973b1

    sign-node-macos: re-zip from the package directory, not the staging wrapper

  7. 9d10e155

    sign-node-macos: re-stamp the zip's own inventory after signing

  8. a8bf17ae

    Move the game interface out of the public source export

  9. d7092ace

    Fix the stand-in page's phone-gate regex

  10. aa020326

    Stand-in: land the camera on the base and hit-test in cell space

  11. 8bbe6a04

    Public build: no logo, white indicators, visible selection

  12. 333da6c8

    Desktop: drop the white plate behind the header mark

  13. 88c5ea1b

    Public build: centre-click selects, disabled renderer degrades silently

  14. a7bcd14e

    Desktop: the header mark reads white without the plate

  15. 12e40ea2

    Logo: centre the R in the slab and balance the wordmark on its plate

  16. 1a42c255

    Desktop: name the app Redline Wars and put exit at the bottom of the app menu

  17. 1152cf5d

    Landing: replace the Suriname aerial photo with own artwork, drop its credits

  18. 2df2ad82

    desktop: regenerate the app icons from the recentered R [skip ci]

7 commits
  1. a2b7ac68

    desktop: scope the packaging map filter to the presentation bundle

  2. e42a2633

    Community placement on standing nodes, spectator-admin rooms, idle demotion

  3. e18ab063

    story: the commit timeline, every commit on the released line

  4. 5bc1c467

    companion: repair passes at support tier; map markers wear the legend glyphs

  5. 3ed9b2cf

    story: the timeline's grey line gets its makers named

  6. 7360ec11

    mp: rooms count seats, not connections · spectator hosts and live host control

30 SEP – 1 OCT · 46 COMMITS

Fixing the room, qualifying the release

Observer hosts, removable AI, room capacity, ready state and promoted admins are checked through real lobby interactions. Temporary lobby chat ships through the engine. Clean-checkout and packaging fixes carry the same qualified game into the browser and desktop downloads. Follow-ups repair the public entry URL and its startup-script security hash, remove bridge-test patrols from a playable map, and connect the ambient fence rails to their posts.

15 commits
  1. 3fabf103

    story: one Opus 5 line, the grey line split, and /story/ stops downloading

  2. 27197e69

    mp: the lobby counts seats, not people · spectators watch

  3. 20e2ab0d

    landing: wire the status page to the live Grid

  4. 1bdea0ae

    mp: survive the Connected-before-lobby-sync gap on high-RTT joins

  5. 127dd139

    mp: a host and a table of bots can finally start · bots seat ready, AI removable, standing rooms solo

  6. aef43d05

    ranked: re-pin the E2E release gate for simBuild 3fe0cba30479

  7. 9aad742c

    test: the start-gate source gate mirrors the bots-count-as-seats rule

  8. 101ac125

    fix: finish desktop lobby and align shared build lane

  9. f63bbb3e

    fix: clear desktop match state before returning to landing

  10. ecb69d1e

    test: isolate fix candidate packaging and preserve gate evidence

  11. 5a4a3fbb

    ci: export canonical RA catalog before clean node packaging

  12. 97ddee8c

    release: wait for qualified clients before switching backend versions

  13. a3367f1f

    packaging: read Windows ZIP manifests through native archive API

  14. edf90517

    release: refresh Mac node content manifests after signing

  15. a22a4dec

    release: block downstream deployment after rejected full preparation

31 commits
  1. 4076151c

    fix: restore public game boot CSP and canonical entry URLs

  2. f989254e

    story: bring the release timeline and current status through October 1

  3. d254836d

    fix: keep River Crossing armies at their spawns and support fence rails

  4. 59c7a113

    gate: qualify strict frame cadence before publishing the spawn fixes

  5. 9c0ecfe5

    story: record the spawn and fence repairs in the final timeline

  6. 2af0a4d6

    ci: install pinned Chromium before strict performance comparison

  7. ebb5a773

    story: include the final qualification repair in the commit timeline

  8. f2546397

    gate: use the visible compositor for both immutable cadence profiles

  9. c2387d35

    test: wait for the real loading overlay before lobby clicks

  10. 2bebbd8a

    story: record the completed release gate repairs

  11. 38f30f25

    ui: give the multiplayer lobby disclosures and visible chat

  12. dbac4fe3

    story: show the lobby redesign and strict release hold

  13. ec67d154

    story: remove the three October 1 highlight cards

  14. 94e4b04b

    fix: compare the same timed camera sweep in the cadence gate

  15. 785f70b8

    fix: isolate the benchmark camera from desktop pointer input

  16. 7b968ae1

    build: recover verified graphics from the stable release

  17. 8371d408

    build: retain exact graphics exports when inputs are unchanged

  18. 1a911207

    build: publish the verified stable graphics baseline

  19. 605b25fe

    build: pin graphics to the exact tested stable export

  20. 327b6ab2

    story: update the timeline through stable graphics preservation

  21. b548323a

    test: qualify settled full-resolution gameplay on the same compositor

  22. 7f5ef223

    test: measure fresh native armies after fixed renderer preheat

  23. afdce753

    test: wait for fixture servers and retain browser failure evidence

  24. a97ed120

    docs: keep the build timeline current through fixture qualification

  25. b5aea175

    test: isolate benchmark input and establish its camera after world warm-up

  26. 580a6883

    test: settle new-world notifications before camera warm-up

  27. 77d3934a

    ui: release the clearer multiplayer lobby on the existing game build

  28. cd95eca2

    release: record the isolated lobby deployment on the fix branch

  29. cc3604c1

    docs: update the timeline through the isolated lobby release

  30. 3dd365b8

    gate: judge frame cadence on delivered frames with a calibrated margin

  31. f11022a9

    docs: update the timeline through the calibrated cadence gate

2 OCT – 4 OCT · 7 COMMITS

The measured release and its version

The physical Mac checks are calibrated on delivered frames, the October game release reaches browser and desktop downloads, and the public site names the same Alpha v0.1.0 product version across its pages. On 4 October, refreshed desktop installers follow the site label without changing the measured game runtime. The source tag remains a separate code identifier.

1 commit
  1. 2a2a079a

    Calibrate delivered-fps gate with display skips and archive release evidence

6 commits
  1. 77af0b0b

    release: align official game version across site and desktop

  2. 22fe447c

    release: block landing-only stage drift

  3. fdc447e7

    docs: align release story and current version guidance

  4. 6620a04e

    docs: record live browser join policy

  5. 1d455ba9

    story: record Alpha desktop release across five platforms

  6. a8a11d4a

    docs: scope multiplayer phase status to verified release

5 OCT – 7 OCT · 71 COMMITS

Voices in the room

Freehop, the open-source calling SDK we built for this game, enters the battle: the call bar, roster cameras, per-peer mute, and the call policy riding the room directory. Riki is re-voiced with an owner-designed expressive commando bank, the battle and victory songs land, and a music page ships the soundtrack. Alpha v0.2.0 carries calling into the browser and desktop releases, the ranked gate is re-pinned through a real match, and the release is qualified on the physical Mac.

51 commits
  1. c8262b63

    feat: integrate Freehop alpha.2 and player history

  2. 65626321

    test: wait for settled High render quality before battle timing

  3. a4e41cc3

    fix: include verifier dependencies in private Freehop payload

  4. ea3362c4

    fix: checkpoint API updates and activate private Freehop qualification

  5. d5cbf65c

    fix: track private firewall changes before mutation

  6. 971b23b0

    fix: clarify recorded player counter on Grid status

  7. 5a87fd54

    fix: restore public game bootstrap CSP compatibility

  8. d62f1f1b

    feat: add browser requirements and troubleshooting help page

  9. ab7bb34c

    fix: connect JOA QR links without manual code entry

  10. cd7f73a6

    fix: restore companion QR route and clarify mobile support

  11. bf1bdfc0

    fix: preserve the JOA companion websocket edge route

  12. 31c94f30

    fix: bound JOA game presentation hotfix with CSP and rollback checks

  13. 778fb45c

    fix: make JOA device roles directly selectable

  14. b4f5457c

    test: start JOA acceptance through the actual game UI

  15. ab0e61a4

    fix: repair Electron USB checker and add private network probe

  16. 322074eb

    fix: prepare current desktop shell for isolated Freehop acceptance

  17. d7981edb

    fix: wait for actual desktop engine readiness and show game version

  18. 870d7678

    fix: route online hosts through relay and reset reconnect diagnostics

  19. da89f17d

    feat: add voluntary community support page

  20. f4422d1a

    Restore newer landing design with current features and support destinations

  21. cb72d916

    Style support buttons and keep donation QR codes visible

  22. 1239fb68

    Keep beer icon inline and align expanded support panel

  23. 7afdb5a1

    fix: wait for native lobby sync before claiming player seat

  24. 3db6c1dd

    Give Hear Riki the new expressive commando voice

  25. 9cffb487

    Add battle songs and local victory and defeat soundtracks

  26. ff73977e

    Give Riki expressive voices and authoritative combat reactions

  27. 9668ec4b

    Use owner-designed rikie voice in landing previews

  28. b59b478e

    Replace all Riki clips with owner-designed American commando voice

  29. b2de668b

    Match Riki rifle preview to game recording and playback pitch

  30. 91ab2193

    Keep Riki rifle sound when armament binding is unavailable

  31. 6914b97d

    Reuse landing typography throughout community support page

  32. 1e7deade

    Play JOA welcome on the approved mobile companion

  33. 3b20cd11

    Restore complete build story and commit timeline with a mandatory publication gate

  34. 2f6f0df3

    Merge current public HUD and JOA with Freehop, music and owner Riki release

  35. 9280bd49

    Merge mobile JOA welcome, QR pairing and role controls

  36. 58fe3fa8

    Merge current website, support, help and restored complete story timeline

  37. c2f1fca8

    Gate unified releases on source ancestry, active HUD features and current website version

  38. e3c7831e

    Verify native JOA lobby admission and preserve private QR authority across packaging

  39. fd07728c

    Validate landing version against installed game so old labels can be repaired

  40. ee4e584d

    Block old branch deployments and stale release reruns before production mutations

  41. 0acf70a9

    Bind private qualification to combined source and fix native five-player test readiness

  42. 988f7b26

    fix: publish support page and verify all landing routes

  43. f29adb99

    test: assert validated staged edge publication

  44. d26a6b27

    docs: record verified removal of superseded worktrees

  45. 0c200155

    Finish Freehop HUD integration and ticket recovery with release checks

  46. 5a5676ef

    Route online hosts through their media authority and switch idle nodes safely

  47. 84b42bc6

    test: stage full battle outside the integrated call HUD

  48. 29e769cf

    docs: bind Freehop acceptance to the current candidate

  49. 1334569f

    fix: bind desktop qualification to exact shipped AppBundle bytes

  50. 6ba624a3

    fix: keep accepted game connection transitions silent

  51. 86462517

    fix(ci): prepare canonical Electron artifacts in clean release jobs

16 commits
  1. 51a9fd62

    feat: Freehop call media policies, HUD roster cameras, soundtrack page

  2. f30f2004

    feat: merge steelseed engine improvements into the unified release

  3. 40575d56

    feat(landing): ship the full soundtrack on the music page

  4. da36a8bf

    feat(desktop): --skip-update-gate dev flag for locally built AppBundle testing

  5. f565fab2

    fix(desktop): brand the dev-run dock and window icons

  6. cd33c01a

    feat(freehop): per-peer mute in the player roster (freehop alpha.3)

  7. 5aaeac73

    fix(host): the room's call policy rides the directory · joiners see the call bar

  8. d78b3f09

    fix(desktop): dev icons resolve via import.meta.dirname · __dirname is undefined under ESM

  9. 43368602

    fix(hud): the per-peer sound toggle is clickable; row grammar is [dot][name]…[sound][ping][bars][ms]

  10. 1debdc6b

    release: game version alpha v0.2.0 across site, desktop and relay

  11. d7762e75

    docs: alpha v0.2.0 corresponding-source tag is v2026.10.06-1debdc6b

  12. fc25b180

    fix(ci): Windows checkout portability and version-derived landing assertions

  13. 6d75dbdd

    fix(tests): the version gate test derives the label from game-version.json

  14. 8d5965f9

    fix(landing): sharp 0.35.5 for the librsvg advisory (CVE-2026-96889)

  15. d4a6a6eb

    fix(tests): carry the call-policy radio and pinned-session routing in the HUD gates

  16. f448f029

    ranked: re-pin the E2E release gate for simBuild 0d53f2930e33

4 commits
  1. c74c72f6

    fix(tests): derive the qualification profile expectation from path.join

  2. 71652e47

    export: classify the 0.2.0 spine services, battle harness and release packaging

  3. 021cf49e

    docs: alpha v0.2.0 corresponding-source tag is v2026.10.07-c74c72f

  4. 7446f033

    docs: the shipped 0.2.0 downloadables carry source tag v2026.10.07-021cf49