← all pages

Content engine: the design

Stickbash content engine + Studio (design)

Date: 6 Nov 2026. Owner: ENGINE (a lane under basha-21, the LIVE coordinator). Status: APPROVED by the user, 6 Nov 2026. All 15 decisions are made (D14 and D15 by the coordinator as technical calls). The four [call]s stand. The implementation plan is docs/superpowers/plans/2026-11-06-content-engine.md.

Marks: [user] = the user decided (6 Nov); [call] = my call, open to the user's veto; [ask] = it was in the decision list at the end, and every one is now DECIDED.

Code citations are file:line in the game repo StickFight-online-train7 (branch live-v4train7, StickBrawl/Assets/_Game/Scripts/..., shortened to Scripts/...) and in this repo (server/...).

This changes one earlier non-goal: the 28 Oct live-game design said "Downloadable content packs. Content ships in app updates (§14.4), but the catalog stays ready for them" (docs/superpowers/specs/2026-10-28-live-game-design.md:46). From now on new content is pushed from our server.


Summary for the founder (one page)

What we are building. One engine inside the game that plays new content from our server with no app update: maps, kill films, skins (with everything attached to them) and taunts / emotes. It is a fixed set of building blocks shipped in the app, about 50 that copy what the game does today plus about 61 new ones the game has never had. These include moving and spinning platforms, switches and doors, portals, lasers, saws, crushers, spawners, rising water and lava, weather, new camera moves, new skin materials and reactions, and taunt props. New items are designs that combine those blocks. A wiring layer and a tiny formula set let designs make new behaviour, all as data with no code downloaded.

Who makes content. AI helpers make about 95 % of it, through a command-line Studio with a full rulebook, precise error messages, preview pictures and videos, test matches against bots, templates and a playbook. A Mac Studio app gives people the same tools visually. Freehand drawing and posing come in a later Studio update.

How it reaches players.

  1. Every item passes automatic checks: every arena, every gore level, every phone tier, frame time within budget, two runs exactly equal, and no bug-looking frames.
  2. A person approves it.
  3. It goes to our test phones, then 10 % of players for a day, then everyone.
  4. Phones download new content at the splash screen with a progress bar. If there's no internet, the game carries on with what it has.
  5. Online rooms check that everyone has the same content and download anything missing first.
  6. Any item can be pulled in one tap. Players keep what they bought: it rolls back to its last good version, or hides until fixed.

What stays the same. Every existing map, film, skin and taunt stays exactly as it is. A proof set (3 maps, 6 films, 6 cosmetics, 4 taunts) is rebuilt in the engine only to prove it matches the originals with zero difference. Players keep the originals.

Safety rules built in. No downloaded code, only data (the app-store rules). The formula set is tiny, and one switch can turn it off. Content can never be gorier than the player's level or the app's rating, never give an advantage, and never run slower than the frame budget. Two phones in a room can never silently disagree.

Build order (the user). One engine at a time, each finished before the next: first the map engine, then the animation (kill film) engine, then skins, then taunts / emotes, then the shared shop / pass catalogue and launch polish.

Cost and time. About 174-212 agent lane-days. The honest calendar depends on how many engine lanes run at once (see §10): the map engine is done in about 9-11 weeks on two lanes, and the whole program is launch-ready in about 7-8 months on one lane, 4½-5½ months on two, or 3¾-4½ months on three. Launch waits for all of it: the engines, the Studio and the proof set.

Decided (6 Nov).

# Decision Choice
D1 New blocks at launch ~61 new T1 blocks
D2 Approval a person approves, enforced by the server
D3 Studio all four kinds at launch, agent-first; drawing / posing later
D4 Missing pack download with a progress bar first (the splash and at room join)
D5 When to download at app open, any network, with a progress bar
D6 Space 5 MB a pack, 150 MB in total
D7 Exact identical, zero difference
D8 Proof set as listed, with the HOODIE
D9 Rollout test phones → 10 % → everyone
D10 Film motion from the kill pages
D11 Gore levels all three authored
D12 Formulas a tiny bounded set, with a kill switch
D13 Pulled item players keep it
D14 File hosting our server behind Cloudflare
D15 Content home a new private repo, stickbash-content

1. Intent

We want more content, faster. Skins, maps, kill films, taunts and emotes, along with everything attached to them (a skin's film, its ammo, its sparks), should reach players from our server with no app update [user].

To do that the game gets one engine. It is a fixed set of building blocks that ships inside the app. Every new item is a document that says which blocks to use and how to set them. Packs of these documents, plus any sound files they need, are pushed from our server. The same game, built for the Mac with its editor turned on, is the Studio. That is where we (our team and Claude agents) build content, test it in real matches and publish it [user]. Agents use the same Studio from the command line on the VM, with no screen [user].

What exists today stays exactly as it is: the ~180 kill tracks, the ~155 film scripts, the 32 code-built arenas, the skins and the taunts [user]. A small proof set is rebuilt in the engine only to prove that it can match the originals exactly. Players keep the originals [user]. The shop, pass, rooms and customize screens treat built-in items and engine items the same way [user]. Moving the old content onto the engine can happen later, item by item, behind a switch that can be rolled back [user]. Launch waits for the engine, the Studio and the proof set [user].

2. Goals, non-goals, anti-goals

Goals

Non-goals

Anti-goals (things the design must never allow)


3. What exists today (the study)

3.1 Kill films: what is data and what is code

A page kill has two halves:

  1. The track (data). Resources/KillTrack/<weapon>_<id>.bytes holds every gore level in one file (Scripts/Core/PageTrack.cs:6-9, format Scripts/Core/PageTrackV2.cs:8-14). It is baked from the HTML kill page by tools/killport/ (bake recorder → pk_pack.py text → pk_pack2.py binary). It runs at 60 Hz (PageTrack.cs:11). It carries the two bodies' 13 joints per frame, the camera (x, y, width, rotation, shear), the speed curve, the weapon rows, the alive / hide / head-gone / ragdoll flags, the events (rounds, flashes, cuts K, blood, soul, cues S, fx T), the multicam views, split screens, panels, whip pans, pips, grades, real grades, key lights, warm spots, grain, contact shadows, material looks (gold / frost / char / tint), blood styles, death cracks, text, trails, piece paths, draw order, and the page's full sound (voices A, beds B, ducks N, library L) (PageTrack.cs:12-120). The v2 file is already integer-quantised: every number is q = value × 10^4 and positions sit on a 0.01 page-unit grid (PageTrackV2.cs:11-14). A decoded level costs ~0.2-0.6 MB, and a match's film pool is decoded on a worker thread (PageTrack.cs:201-232).
  2. The film script (code). Each film registers a PageKill.Def from its own file through the auto-registration row (Scripts/Core/PageKills/PageKillRegistry.cs:4-12). The Def has plain fields: key, name, weapon, rarity, arena, wire id, slot, skin family, gap, fx scale, ink-in / out, held-hide windows, voice edits, beds, single camera, stay mode and so on (Scripts/Core/PageKill.cs:44-153). It also has C# hooks: cue, step, draw, drawUnder, drawInset, bodyBack, poseFix, fx, begin / end, drawAfter, drawAfterUnder, room, piecePlace, dryCut, supports, trackOf (PageKill.cs:60-176).

How much of each film is code? I tallied the 155 files in Scripts/Core/PageKills/:

What the film does Files What it is today
Motion, camera, clock, rounds, blood, soul, page sound 155 track data
Nothing beyond the Def's fields (e.g. SixShooter.cs, 22 lines) 9 pure data
begin / step (its own state each step) 145 / 113 code
draw / drawUnder / drawTop (its own props: slabs, swords, hats, marks) 140 / 87 / 17 code
drawAfter / drawAfterUnder (a prop or mark that outlives the film) 44 / 24 code
cue (handles or drops a sound cue) 130 code
edits the track's events (tr.ev, e.g. no head pop at MEDIUM) 43 code
uses a real game weapon as a prop (Weapons.Get() 44 code
voiceEdit (drops or rescales page voices) 47 data
room (a room rule tied to real surfaces) 13 code
skin films (slot = PkSlot.Skin) / mythic 64 / 1 data field
generic across guns (supports / trackOf) 11 / 8 code (small lambdas)

The custom drawing is all vector shapes from the game's one shape batch: Line 535 calls, Circle 249, RawQuad 135, Ellipse 121, Box 46, Glow 39, SoftEllipse 35, then BoxAxis, Arc, Tri, Ring, Star. Colours go through PageKill.PageColor* (~350 calls), and cuts go through FinisherCut.*.

Finding 1. A film's motion is already data. What makes 146 of the 155 scripts code is a small, repeating set of jobs: draw shapes tied to joints and time, keep a few timers, react to a cue, patch the track per gore level, put a real weapon in a hand, leave a mark behind. Each of those jobs can be a block.

Where the films run: FinisherStart → PageKillRotation.Try → FinisherPool.PagePick (PageKill.cs:25-26). The older KillCine timeline library (Scripts/Core/KillCine.cs:6-22: actors, camera language, HUD layers, spot solver) still exists beside it. Gore has six internal levels, and players pick LOW 0, MEDIUM 2 or EXTREME 5 (Scripts/Core/KillSysGore.cs:5-12). The track holds one block per level, and a missing level falls back to g5 (PageTrack.cs:172). Film quality tiers are LOW / MED / HIGH, with a film governor that steps down under load (Scripts/Core/KillCineQuality.cs:48-58). Sound plays the page's voices on the film clock through the page library, the gameplay gun clips and the beds / ducks (Scripts/Core/PageKillSound.cs:6-13).

3.2 Kill films online

The host picks the film and sends one FilmStart at film time 0. Every phone plays the same film locally as "a pure function of the track + time", with a seeded Rng. Gore is local, and the end-state hash covers only gore-independent state (Scripts/Core/PageKillNet.cs:7-20). Films are named on the wire by a u16 wire id from an append-only table (Scripts/Core/PageKillRotation.cs:775-800). The catalogue of key @ wire is hashed into NetProto.ContentHash (Scripts/Net/NetProto.cs:147-160), together with the weapons, moves, arenas and items. A mismatch refuses the join with "host and you run different game builds. Update both" (Scripts/Net/NetHost.cs:413). Protocol is 144 (NetProto.cs:9).

3.3 Maps

3.4 Skins, cosmetics, taunts

Finding 2. All of the game's art is SDF vector shapes from one shader and one dynamic mesh per layer (Scripts/Render/ShapeBatch.cs:9-13: capsule, rounded box, arc, triangle, glow, star, ring, soft, ellipse). The only non-code art is a handful of textures (Resources/Art 744 KB, ItemArt 1.8 MB) and the sound (Resources/Audio 98 MB, 172 kill-library files). So most new art can be pure data (shape lists), with no AssetBundle and no shader at all. Only new sound files, and rare raster art, need bundles.

3.5 Live config, catalogue, server

3.6 Platform facts


4. The idea in plain words

Think of it as LEGO:


5. Architecture

 STUDIO (Mac, internal build)            HEADLESS CLI (VM / M2 / Mac)
   edit docs · preview in real match       studio.sh validate|compile|preview|gate|pack|publish
        \                                     /
         └──── content repo (git: JSON docs + source sounds) ────┘
                              │ compile (quantise, resolve refs, bake bundles per platform)
                              ▼
                     PACK  (content .sbp, sha256-named · bundles per platform · envelope .sbe: report + signature)
                              │ GATE (schema, budgets, determinism, perf per tier, arenas,
                              │       gore + rating, look checks, exact-match for the proof set)
                              ▼
   sbapi content registry ── content list (channels, rollout %) + kill list (only grows); human approval for live
                              │
          content.stickbash.com (VM origin, Cloudflare in front, immutable URLs)
                              ▼
 PHONE: fetch → verify hash + signature → store → ENGINE RUNTIME (app-shipped blocks)
        → shared catalogue (shop / pass / rooms / customize) → matches (pack-set hash in the join)

5.1 The runtime

The runtime is one C# module inside the app (Scripts/Engine/):

5.2 The block library

A block is an app-shipped C# class with a stable id (film.prop, map.pendulum, ...), a typed parameter schema (every number has a unit, a grid and a range), a cost formula, a tier rule (what it drops at LOW / MED), a gore rule (which levels it may appear at, and what it becomes below that level), a determinism class (sim: changes the match state, host-authoritative, hashed; look: drawing / sound only, may differ per phone) and its attach points. The library has a version number (ENGINE_API). It only ever grows: block ids and parameter ids are append-only, a removed block keeps its id, and a parameter's meaning never changes (§5.8).

The families below cover everything the study found in today's content. Every row names the existing code it wraps, so the first version is mostly a wrapper around tested code, not new behaviour.

A. Common blocks (every kind of content)

Block Parameters Wraps today
shape kind (line, circle, ellipse, soft ellipse, box, rounded box, quad, arc, tri, ring, star, glow), size, stroke, fill, outline, colour (a palette slot or RGBA), layer (under / fighters / over / top / inset-only), alpha ShapeBatch
rig a tree of shapes / rigs with local transforms; an anchor (a body joint 0-12, a weapon part, a world point, a map element, a film piece, the camera); flip with facing per-film Draw code
driver drives one parameter of one block over time: a keyframe curve (time → value, eases from the TE set + cubic Bézier), follow an anchor, between two anchors at u, orbit, seeded noise (amplitude, rate), step list, bounded repeat (n ≤ 64) the hand-written math in step / draw
palette named colour slots; page colour mapping (PageKill.PageColor*), gore colour, skin-theme colour (Gold / Ice main / light / dark) PageKill.PageColor, SkinPremium.Main
look a body or prop material: tint + k, gold, frost, char, outline ink, rim / back light, gloss; spread from a joint over time track O look, Def.lookInk, bodyBack
text a comic word or glyph: text (from the pack's string table), style, burst colour, size, rotation, anchor, life PageKill.Glyph, TWord
sound a voice (library id or pack clip, vol, rate, pan, low / high pass, offset, dur, fades, loop, bus), a bed, a music-kit chunk (bed / pulse / drum / riser / hit), a duck / hush PageTrack.Voice / Bed, KillBed, the music kit
fx an emitter from the engine's fixed presets (dust, sparks, debris, coins, leaves, confetti, snow, shards, flakes, smoke, fire, glow, steam, ink), count, spread, life, colour; physics drop with bounce / rest KillFx, the track's T fx
grade preset (CINEMA, SLOWMO_COLD, NIGHT, IMPACT_SPLIT), filter ops (sat, contrast, bright, hue, gray, sepia), tint, vignette, grain, key light, warm spot, light shaft + projected shadow track O real / grade / grain / warm, COLD-BLOODED's shaft
camera shot (centre, width, roll), follow body / joint, cut, whip, shake, punch, zoom; insets / panels / split (master flag, slide side, delay, gutter); single-camera switch track camera + views, Def.singleCam
clock speed curve keys, freeze, hit-stop, slow-mo sound pitch / low-pass rule track sT / spd, TimeCtl
rng a seeded choice among variants (weights); the seed is the instance's own, never the global Rng the film Rng
counter small integers / flags per instance (set, add, compare); part of the logic graph's ≤ 32 state nodes (§5.5) the films' static state

B. Film blocks

Block Parameters Wraps today
film.take a v2 track (per gore level) imported from a kill page bake or recorded in the Studio; its length in ticks PageTrack
film.cast roles (killer, victim); weapon binding: its own weapon, a gun class (Compact / Long / Heavy / Flame / Explosive) with a track per class, any weapon (mythic); rotation slot (round-end, skin family, mythic); rarity; rotation weight Def.weapon / supports / trackOf / slot / skin / anyWeapon, PageGunMap
film.fit gap range, start melt, stay mode + aim fix, floor width, headroom, a needed surface (wall / ceiling / ledge / drop by element kind, with a shift range), realm windows, last-resort kind Def.minGap / maxGap / stay / room / realmScene / lastResort
film.weaponprop a real game weapon (by id, or "the killer's", "the victim's") as a prop: in a hand (body, hand, grip offset), flying on a path, dropped (lands flat, stays), ink-in / ink-out, held-part hide windows inkIn / inkOut / heldHide, the 44 Weapons.Get props
film.prop a rig that appears / disappears at ticks, with states (whole, cracked, holed, broken) switched by triggers, cast from ink, collides with the floor, stays after the film for N s DRIP's slab, THE X's hat, the ink swords
film.mark a floor / wall decal (shape rig) placed at an anchor at a tick, kept after the film for N s or until the next film THE X's wall X, DRIP's drip line, drawAfterUnder
film.cut a FinisherCut part at a tick (per gore level), sealed (no blood, cap colour, unseal tick), piece path shape / rest / flat / removed, piece tint track K, dryCut / sealColor / unsealScene / piecePlace / piecesFlat / piecesGone
film.blood style switches (main / dark colour), per-tier caps for drops / splats, keep red O bloodstyle, lowDropCap / lowSplatCap
film.soul strength, shown-while-hidden soulAlpha / soulWhenHidden
film.trackedit a declarative patch on the take per gore level: clear head-gone frames, remove events by kind + tag + time window, drop / rescale voices, clear a hide window, add a cut the 43 tr.ev edits, voiceEdit
film.posefix an IK reach of one limb to an anchor over a window poseFix
film.order draw-order windows (killer over, victim over, head over, inset over) killerOver / victimOver / headOver / insetOver
film.handoff the end state: the victim's death pose source, what stays, the GLOAT window Finisher end, the GLOAT pre-select

C. Map blocks

Block Parameters Wraps today
map.frame bounds, closed / open, follow / fit-all, size badge, camera / kill / focus areas, sky ceiling, players 2-8 ArenaDef.Bounds and the area fields
map.solid floor, box, ledge (one-way), slope / stairs (filled), shell; look id; material (turf, stone, wood, metal, ...); ice; conveyor speed PlatDef, the DSL
map.traverse ladder, jump pad (launch vector), lift (stops, period, dwell, cable top), cart, low-g zone (gravity) LadderDef / PadDef / MoverDef / ZoneDef
map.liquid water (depth, currents, vents), lava (top, damage, bounce), sea out-line MapWater, lavaTop
map.spawns spawn points, extra spawns (5-8 players), start weapons, racks (Spot), hatches, drop range SpawnsM / Extra / Spot / Hatch / startWeapons
map.prop crate / barrel / static crate; reserved (parked until a cue releases it) PropDef, MapReserve
map.piece a kinematic platform (MapPlat): size, one-way, surface; motion = path (points, period, dwell), pivot (angle curve, or weight-driven seesaw with spring / damping / caps), breakable (hits to break, debris, rebuild cue), crumble (stand time → shake → drop → back after N s); bot nav hint (walk / avoid / ride) MapPlat, Seesaw / Crumble / Wreck code
map.hazard pendulum (pivot, length, radius, the castle swing curve Level.PendAngleAt, phase, solid), kill zone (pit / grinder / spikes / sea), damage area (damage per touch, burn, bounce), push (wind gusts, current), magnet (pull targets by MapMetal, strength); every hazard follows the room's HAZARDS switch Level pendulum, lava, wind, MapMetal
map.timeline cues at fight time (ticks) × the MAP EVENTS pace (NORMAL 1, CALM 1.5, CHAOS 0.65), calm-skip / chaos-only flags, quiet-stretch hold, loop MapTimeline
map.event a named event: warning (beacons, banner text, sound, length), then phases of actions on pieces / hazards / props (move, drop, swing, rebuild, release, show / hide, set a counter), each phase with a length; declared payload fields (ints) MapEvent + Write / Read
map.art backdrop layers (parallax), decor kinds (DK.*), signs, theme palette + surface styles; art rigs on pieces Theme, MapLook*.cs, DecorDef
map.look dim, saturation, locked night 85-100 (same shape as ops.maplook) MapLookConf
map.elements the 58k element tags (kind, tier, hazard, uses) for every element, used by film.fit and exported to the kill pages KillSysMapData, MapExportLab

Bot navigation is not a block. The runtime builds NavGraph from the solids and traverse blocks, exactly as for code maps, and pieces add their nav hint.

D. Cosmetic blocks

Block Parameters Wraps today
cos.item slot (any CosmeticSlot), rarity, source, name, description, showcase colour, set, hidden, film variant of CosmeticDef
cos.art a rig attached to a body joint / the head / the hands / the back, or a weapon's parts (per weapon: part index → recolour, overlay rig, glint) HatArt / OutfitArt / ... recipes, CosmeticsPlain
cos.premium the attachments: film (a ref to an engine film or a built-in key), ammo theme (round shape rig, trail, impact fx), hit sparks (fx preset + palette), death burst (built-in skin-kill family or engine film), feed icon (rig) SkinContents
cos.filmrule how the item shows in films / previews (mode, max count, gore) CosmeticFilmRule

E. Performance blocks (taunts, emotes, win dances, gloats)

Block Parameters Wraps today
perf.pose keys: tick, ease (TE), torso / head / drop / hip offsets, hand / foot targets in TF frames, elbow / knee flips TauntRig.TB
perf.face a TFace per window TauntFaces
perf.words TWord rows TauntWords
perf.prop attached rigs with windows (the L sticker, the chai cup) TauntProps
perf.clip a voice clip (built-in id or pack clip), start tick TauntAudio
perf.gloat the variant played near a KO'd body: approach (walk to range), sit-on / stand-over / point, a mark that stays TauntGloats
perf.kind emote (hands-only, wheel), taunt (full-body), win dance TauntKind

The tables above wrap today's content. They are the floor, not the launch library. A block that isn't in the app can't be pushed later without an app update, so the launch library also carries a broad set of blocks the game doesn't have yet (§7, "NEW BLOCKS BEYOND TODAY'S GAME") [user, 6 Nov].

How flexibility grows. Blocks compose: any rig on any anchor, any driver on any number, any variant on any setting, and any logic-graph wiring of sources to sinks (§5.5). New behaviour comes from new combinations, with no new code. When a design needs something no block can say, we add a block or a parameter in the next app update (§5.8). Packs that need it are offered only to apps that have it. That is a normal app update, which the store rules allow, and it keeps every pack declarative.

5.3 The content document model

Every item of every kind is one document with the same envelope. The authoring form is JSON in the content repo (diff-friendly, agents can write it). The compiled form inside a pack is binary.

{
  "doc": "film",                          // film | map | cosmetic | performance | (shared) rig | palette | soundset
  "id": "eng.smg.night-shift",            // stable, namespaced; never reused
  "rev": 3,                               // author's revision (the pack hash is the real identity)
  "engine": 1,                            // the minimum ENGINE_API this doc needs
  "name": "NIGHT SHIFT", "rarity": "EPIC",
  "rating": { "gore": [0, 2, 5], "flags": [] },   // gore levels it authors; content flags (§5.10)
  "uses": ["eng.pack.core-props/rig.ink-sword", "builtin:weapon/smg"],   // its references, resolved at compile time
  "variants": { "gore": {...}, "tier": {...}, "mirror": true },
  "blocks": [ { "b": "film.take", "id": "take", "p": { "track": "take:night-shift" } }   // a pack-local ref, never a file path,
              { "b": "film.prop", "id": "slab", "p": {...}, "states": {...} } ],
  "timeline": [ { "at": 54, "do": "slab.show" }, { "on": "cue:shot3", "do": "slab.state:holed" } ],
  "studio": { "notes": "...", "thumb": {...} }     // stripped from the compiled pack
}

Rules:

5.4 Attachments and composition

5.5 Timelines, triggers and the logic layer

One timeline core and one logic layer serve every kind of content. The user asked (6 Nov) for "additional things too which is not in game now (like for example additional elements or logics or things for maps etc)" [user]. Fixed blocks alone can't express new behaviour, so it has to come from combining them. The logic layer is how blocks combine, and it stays data.

Time. Time is an integer tick. Films use film ticks (60 Hz frames of the take, the same grid as PageTrack). Maps use fight ticks (the fixed step). Performances use performance ticks. No content time is ever a float.

The simple form: a timeline + rule list. Most films, skins and taunts need only this.

The full form: the logic graph. Maps and complex films use this. It works like wiring switches, timers and lamps on a board, and the Studio shows it as a node graph:

State machines for pieces and props. These are a finite set of named states with timed or triggered transitions, which the logic graph can drive. The Wrecking Yard plank (up → smashed → rebuilt) and DRIP's slab (cast → whole → holed) work this way.

5.5a The tiny formula set (DECIDED, D12, 6 Nov)

The user chose (B), overriding the recommendation, because it makes AI-made content more flexible [user]. A formula is a bounded arithmetic value that may replace a constant in a parameter slot that allows it. Example: an aura's glow = clamp(lerp(0.2, 1.0, speed / 6m/s), 0, 1).

5.6 Gore and the age rating

5.7 Determinism by construction (the Half-float class made impossible)

These rules make the 6 Nov class of bug impossible to write:

  1. Integer grids at authoring time. The compiler quantises every content number once, onto a declared grid: ticks, mm, 1/65536 turns, and 1e-4 units for dimensionless numbers (as v2 tracks already do). The runtime stores these integers and converts to float only at the very end, for drawing and physics input. Any "sample time" (an end-hash tick, a film length, a cue time) is an integer that every phone reads from the same pack bytes.
  2. The wire carries no content floats. Engine content travels in new messages written by a typed writer, EngineWriter. It has only ContentRef, Tick (u16 / u32), Seed (u16 / u32), small ints, enums and the snapshot's existing quantised position type (Pos: int16 mm). It has no F32 and no Half method, so a lossy float can't be added by mistake.
    • An engine film is started by EngineFilmStart, not FilmStart. It carries the film ref, the seed, the fighters, their feet and floor as Pos (mm), the room shift in mm, the start, length and hash points as ticks, and the host's start as a snapshot tick.
    • Built-in films keep FilmStart exactly as it is (PageKillNet.cs:43-58, Half and F32 fields included). The engine does not touch legacy content, so that path keeps today's rules and today's fix.
  3. Two kinds of online state. The rule for each is different:
    • Replicated state is computed on the host only and sent quantised: map pieces, hazards, the logic graph, spawns, props that collide, event phases. Phones never need to agree on how it was computed, only on the bytes they receive. Each phone poses its own picture from those bytes with a pure function (state ints, tick), the way WRECKING YARD's balls follow the clock (MapWreck.cs:23-25). Float maths inside that pose function (the pendulum's trig in Level.PendAngleAt, Box2D contacts) stays on the phone and is never compared.
    • Recomputed-and-hashed state is what every phone works out for itself and then compares: today only a film's online state (§5.6). It must be integer, or quantised to the wire grid before it is hashed. Anything computed in floats (IK in film.posefix, a film prop's floor bounce) is look and never enters the hash.
    • look blocks (gore, tier extras, fx, sound, grade) may differ per phone and never enter a hash.
  4. A generic event payload. A map.event and the logic graph declare their state fields (ints with ranges). The runtime writes and reads them generically into the level snapshot (MapEventSnap), so there is no hand-written Write / Read that can drift between host and client.
  5. Seeded randomness only. Random draws come from named streams seeded by a stable hash (FNV-1a over the id bytes, never string.GetHashCode) of the round or film seed, the content id, the instance and the stream id. The gate records each stream's draw count per tick.
    • For the proof maps, a director-compat stream reproduces today's shared map-director Rng and its seed formula exactly (MapEvents.cs:192, :314). PALLET's x (MapWreck.cs:385) and Belt Line's crate x (MapBeltLine.cs:361) draw from it in today's order.
    • New content never uses the compat stream.
  6. Golden determinism runs at the gate, in three steps:
    • Every document plays twice in one process; the per-tick state hash must match on every tick, not only at the end.
    • It plays again on the other desktop build (Mac vs Linux headless).
    • Before it can leave the test channel, it plays on the reference phones (one iOS ARM, one Android ARM), because a Mac / Linux pair can't show what ARM maths does.
    • A mismatch in recomputed-and-hashed state fails the gate and names the first tick and block.
  7. The end hash uses the same rules. The film end hash samples at the doc's declared end tick, which is an integer from the pack. Its value is the same at every gore level (§5.6), or the legacy ladder rule applies for imported legacy takes.

5.8 Versioning, pack hashes, compatibility

5.9 Asset delivery (sound and the rare raster)

5.10 The Studio (Mac app)

The Studio is the internal Mac build of the same game (non-SB_STORE, like the lab builds) started with -studio. It is never in a store build. Its screens:

  1. Library: every document in the content repo and every built-in item (read-only, as a reference and a source to copy settings from). Filters by kind, weapon, map and status (draft, gated, live).
  2. Editor: the real game view plus a timeline (tracks per block, keys, triggers, a scrubber at tick resolution, gore / tier / mirror toggles) an inspector (every parameter with its range, unit and cost) and a logic-graph view (nodes and wires, with live signal values while the item plays). Undo / redo. Live reload: saving the JSON re-plays at once.
  3. Try it: drop the item into a real practice match against a bot on any arena (films: force the K.O. with this film, as the KILL TEST does; maps: play the map with 1-7 bots and any MAP EVENTS pace; cosmetics: wear it, see it in films and previews; performances: taunt on the bot's body).
  4. Import: bring a baked track in from the kill-page pipeline (tools/killport), bring sounds in (with loudness and length checks), take shapes from an SVG-like path list, and turn a built-in item into a draft document as a starting point (this is how the proof set starts).
  5. Gate: run the gate locally (a fast subset) or on the VM / M2 (the full gate), and see the report: per-tier frame times, the arena strips, the determinism result, gore / rating, look-check frames.
  6. Publish: pick the pack, the channel and the rollout, and request approval (a person approves live, D2).

DECIDED (D3, 6 Nov) [user]: the Studio works for all four kinds at launch (option A). Freehand drawing and the posing tool come in a later Studio update, and the data model already allows them (§6.7). The user expects AI agents to build ~95 % of content and people ~5 %, so the headless agent interface is the Studio's primary product, ahead of the visual editor (§6). The visual editor is a view over the same documents and commands.

5.11 The headless CLI (agents)

This is the command surface. §6 describes the full agent-first design around it (schemas, errors, preview, test-play, hot reload, playbook).

tools/studio.sh <command> runs the same Studio player with -batchmode -scenario studio:<command> (as cap.sh does for labs). It works on the VM (Linux headless player, CPU-only steps), on the M2 worker and on the Mac (GPU steps: frames, video, perf). Every command prints one JSON report and exits non-zero on failure:

Command Does
new <kind> <id> [--from builtin:<id>] a draft document (optionally converted from a built-in item)
validate <doc> schema, ranges, references, budgets (static), rating flags
compile <doc...> the binary docs + the dependency list
preview <doc> --arena <id> --gore <0,2,5> --tier <low,med,high> [--frames N | --video] frames or an mp4 from a real match
diff <doc> --against builtin:<id> the exact-match report for the proof set (§8.1)
gate <pack> the full gate (§5.12); writes the signed gate report
pack <docs...> the .sbp + per-platform bundles
publish <pack> --channel internal|test|live [--rollout N%] uploads to sbapi (needs a publish token, kept in the VM's secret store, never in the repo)

Agents work in the content repo, which is a git repo of JSON plus source sounds (DECIDED, D15: a new private repo farisbasha/stickbash-content; the layout is in §6.4). They commit by path and run the gate on the VM, and the report is attached to the publish.

5.12 The quality gate is the publish step

publish refuses a pack without a passing gate report that has the same pack hash. The gate is today's PORT GATE (~/StickBrawl-v3/v4/feedback/_tools/PORT_GATE.md) and the in-game kill performance rules, automated and applied to every kind of content:

  1. Static: schema, ranges, references, engine version, bundle contents (§5.9), string table words, static budget (sum of block costs per tier ≤ the kind's budget).
  2. Determinism: §5.7 rule 6, on every variant.
  3. Performance per tier: the reference players at LOW / MED / HIGH (FilmQ.LabForce) and with -perfslow 6, on the Mac / M2 (GPU). The headless Linux VM reports CPU ms only, never p95. Frame p95 ≤ 16.7 ms, max ≤ 33 ms, begin ≤ 16.7 ms, 0 B GC per frame in steady state, no hitch at start or end. Per-block budgets are enforced. A block over its budget fails with its name. Before a pack leaves the test channel it also runs the perf and determinism scenario on the reference phones (an iOS phone and a LOW-tier Android, named in the plan). This is a step in the pipeline for every pack, not a one-off check.
  4. Coverage: films on all arenas (home + every arena that fits, plus a clean refusal where it doesn't) at all three gore levels; maps with 2, 4 and 8 fighters, bots only, at every MAP EVENTS pace, HAZARDS on / off, online host + client; cosmetics on every body style, in films and previews; performances on flat, slope and moving floors, and as a gloat.
  5. Look checks (PORT_GATE §4, the automatic part): limbs through solids, floating or sinking bodies, blood in mid-air or on sky, particles frozen or left after the film, the camera leaving the subject or clipping, black frames, the HUD back after hand-off. A flagged frame goes to the coordinator's review sheet.
  6. Gore + rating: each level's content against the ladder and AgeRules.
  7. Exact match (proof set only, §8.1) and the block tests of every block the item uses (§8.6).
  8. Human look: the coordinator's side-by-side sheet, then a person's approval for live (D2).

When all of these pass, the gate signs the report and the pack's manifest. The phone checks the signature.

5.13 Server registry, CDN, rollout and rollback

5.14 The shared catalogue

5.15 Store compliance (why this is allowed)


6. AGENT-FIRST AUTHORING

DECIDED (D3, 6 Nov) [user]: "most of the time the ai agent will build this, I mean 95% of the time, 5% of time we may manually create maps, so prioritize things make ai agent easier and flexible and customizable".

In plain words. The main user of the Studio is an AI helper typing commands, not a person dragging things. So the Studio comes with:

A change should show up as a picture in under 30 seconds. The visual editor, and later the drawing and posing tools, sit on top of the same files and commands, so anything a helper makes a person can open, and the other way round.

6.1 The schema: one versioned, documented contract per kind

6.2 The command interface: studio.sh and the same commands over JSON-RPC

Every command takes files or JSON on stdin and returns one machine-readable JSON result (plus a short human summary on stderr). Exit codes: 0 ok, 1 errors, 2 warnings only, 3 tool failure. The same commands are served over a local JSON-RPC socket by the running Studio (studio.sh serve, §6.5), so an agent can call them without starting a process each time.

Command What it gives the agent
schema --kind <k> [--block <b>] the schema / one block's reference as JSON
new <kind> <id> --template <t> / --from builtin:<id> a draft from a template (§6.3) or converted from a built-in item
validate <doc> precise errors, each with a JSON path (/blocks/3/p/period), the rule broken, the value found, the allowed range and a fix hint ("period must be 0.5s-60s; you wrote 0.2s: raise it or use map.rotor for fast spins"). It also explains reference errors ("rig.ink-sword not found; did you mean eng.core-props/rig.ink_sword?")
lint <doc> style and budget warnings: over 80 % of a tier budget, a hazard warning under 0.8 s, unused nodes, a logic graph that can never fire a sink, a film prop that never hides, palette colours outside the game's look range, a text string that fails the word check, a map spawn too near a hazard
explain <doc> a plain summary of what the document does over time (timeline + logic, in words). Agents use it to check that their intent matches what they wrote
preview <doc> [--arena] [--gore] [--tier] [--seed] [--at 1.2s,2.5s | --every 0.25s | --video] [--cam fit|follow:<id>] stills (PNG) or an mp4 from the real game, with a frame-index sheet and a contact sheet, for the agent's own visual check
play <map|doc> --bots N --mode <m> --rounds R --seed S [--pace calm|normal|chaos] test-play vs bots. A metrics report: deaths by cause (hazard / fighter / ring-out), time near each hazard, spawn fairness, bot stuck time and unreachable spans, path coverage heat map, round length, event firings, film fits / refusals per arena, perf (p50 / p95 / max)
gate <pack> [--fast] the full gate (§5.12), or a fast local subset
diff <doc> --against <rev|builtin:id|file> what changed vs a previous revision: parameter diffs, cost diff, a visual diff strip at the same seed and ticks, logic-graph diff, and the exact-match report for the proof set
pack / publish --channel internal|test build the pack and send it to our own channels. live needs a human approval (D2)
docs <block> the block's reference page

6.3 Templates and worked examples

6.4 The content repo layout (for agents)

stickbash-content/
  schema/<ENGINE_API>/*.json        generated, committed at each engine bump
  docs/blocks/*.md  docs/playbook/*.md
  templates/<kind>/*.json  examples/proof/*.json  recipes/*.json
  packs/<pack-id>/pack.json         the pack's manifest source (deps, channel notes)
  packs/<pack-id>/<doc>.json        one document per file
  packs/<pack-id>/sounds/*.wav      source sounds (imported by the bundle lane)
  packs/<pack-id>/takes/*.bytes     imported film takes
  out/                              previews, reports (git-ignored)

One document per file, stable key order, and studio.sh fmt to canonicalise, so diffs stay small and two agents rarely conflict.

6.5 Fast iteration: under 30 s from edit to preview still

6.6 The agent playbook

docs/playbook/ in the content repo holds one page per kind (map, film, skin, taunt), each a numbered checklist the agent follows:

  1. Read the brief.
  2. Pick a template or a proof example.
  3. List the blocks to use.
  4. Write the document.
  5. validate until clean, then lint until clean or each warning is justified.
  6. explain and compare the summary with the brief.
  7. preview the key moments.
  8. play (maps: 2 / 4 / 8 bots, every pace) and read the metrics against the targets on the page.
  9. gate --fast.
  10. diff against the last revision.
  11. Write the check sheet (stills, metrics, gate).
  12. publish --channel test and request approval.

Each page also lists the kind's look rules, taken from the existing taste notes (e.g. USER_TASTE.md and the cinematic and realistic action direction for films), the fairness rules for maps (warnings, spawn distance, no unwinnable spots), and the no-advantage rules for cosmetics. These are rules an agent can check, not taste it has to guess.

6.7 Ready for freehand drawing and posing later (no data-model change)


7. NEW BLOCKS BEYOND TODAY'S GAME

In plain words. A pushed item can only use bricks that are already inside the app. If a brick is missing on launch day, no item can use it until the next app update. So the launch app carries today's bricks (§5.2) plus many new ones the game has never had: moving and spinning platforms, switches and doors, portals, lasers and saws, spawners, rising water, weather, new camera moves, new skin materials, skins that react when you hit or win, and taunt props. The logic graph (§5.5) lets us wire them together into behaviours nobody has built yet, such as a door that opens when two players stand on two plates, or lava that rises faster when only two fighters are left [user, 6 Nov]. DECIDED (D1, 6 Nov) [user]: the launch app carries today's blocks plus the ~61 T1 blocks below (option A).

7.1 Rules every new block follows

7.2 Maps: new elements and logic

# Tier Block What it does Parameters Online Budget (LOW)
1 T1 logic.* (the logic graph, §5.5) Wires every block below into new behaviour nodes, wires, constants sim on the host; packed state ≤ 64 B in the snapshot ≤ 256 nodes, ≤ 60 µs / step
2 T1 map.zone An area that senses who is in it: enter, leave, count, team, held time shape (box / circle / poly), filter (fighters, bots, props, items, team), min hold ticks sim, host overlap test; outputs bools / counts ≤ 32 zones, ≤ 3 µs each
3 T1 map.mover A platform, prop or hazard that moves along a spline path (≤ 32 points, open / closed, Catmull-Rom or line), period or speed, ease per segment, dwell per node, ping-pong, start phase, carries riders sim, pose = f(tick, phase, paused-by-logic); predictable ≤ 24 movers, ≤ 4 µs each, 1 collider
4 T1 map.rotor A rotating or orbiting platform (wheel, windmill, clock hand, satellite planks) pivot, arms 1-8, radius, angular speed curve, start angle, reverse on signal, carries riders sim, f(tick); predictable (slopes underfoot as MapPlat.rot today) ≤ 8 rotors × 8 arms, ≤ 6 µs each
5 T1 map.switch A thing you hit, stand on or touch to flip a signal kind (lever, button, target), momentary / toggle / one-shot, cooldown, who may use it, look rig sim, host; state bit in the snapshot ≤ 32, ≤ 2 µs
6 T1 map.door A sliding, swinging or folding solid that opens / closes on a signal piece size, motion (slide vector / hinge angle), open / close ticks, ease, crush-safe (stops on a body), auto-close delay sim, f(open ticks); bot rule: wait while shut ≤ 16, ≤ 3 µs
7 T1 map.laser A beam: fixed, sweeping or blinking ends or pivot + sweep curve, on / off pattern, warning blink ticks, effect (damage / K.O. / push), blocked by solids (yes / no) sim, f(tick, state); raycast on host only ≤ 6, ≤ 15 µs with blocking
8 T1 map.saw A spinning blade on a path or a fixed spot radius, path (via map.mover), spin, damage per touch, sparks, gore cut rule (follows the gore level) sim, f(tick); gore is look ≤ 8, ≤ 4 µs
9 T1 map.crusher A piston or ceiling block that slams down and rises travel, down / hold / up ticks, warning shake, effect (K.O. if pinned against a solid), safe stop on props sim, f(tick, cycle); bot rule: avoid during warning ≤ 8, ≤ 5 µs
10 T1 map.spikes Spikes that pop out of a floor or wall on a timer or signal span, out / in ticks, warning ticks, damage or K.O. sim, state bit + phase tick ≤ 16 spans, ≤ 2 µs
11 T1 map.dropper Things falling from above (rocks, anvils, crates, icicles) with a shadow warning drop points or an x range (seeded pick), object kind (built-in props / rigs), interval curve, warning ticks, effect sim, the host picks with the seeded stream; picks ride the snapshot ≤ 4 in the air, ≤ 6 µs
12 T1 map.spawner Spawns weapons, items or crates at points on a rule pool (built-in weapon / item / prop ids with weights), points, interval, max alive, first-spawn tick, seeded pick sim; props are pre-built and parked (the MapReserve rule, MapNewKit.cs:14-15) so the props block stays in build order pool ≤ 16 parked, ≤ 3 µs
13 T1 map.level Water or lava whose level follows a timeline or the logic graph kind (water / lava / acid look of lava), level curve over fight time, speed, warning, max sim, level = f(tick, state); existing flood / lava rules 1 per map, ≤ 5 µs
14 T1 map.field Gravity, low-g, wind or current zones with a strength that changes area, kind (gravity scale / push vector / swirl), strength curve or signal-driven, gust pattern (seeded) sim, predictable Accel(p) ≤ 8 fields, ≤ 2 µs per fighter
15 T1 map.phase Platforms that fade in and out (blink, alternate sets, appear on a signal) pieces, on / off ticks or signal, warning blink, one-way or solid sim, state bit ≤ 32 pieces, ≤ 1 µs
16 T1 map.tiles A grid of tiles that break from hits or standing and grow back grid, tile size / material, break rule (hits / stand ticks / explosion), shake ticks, regrow ticks, never-break set sim, bitmask in the snapshot (generalises Crumble Tiles) ≤ 64 tiles, ≤ 4 µs
17 T1 map.belt Conveyors whose direction and speed change pieces, speed curve, reverse on signal, warning sim, predictable (today's PlatDef.conveyor made dynamic) ≤ 8, ≤ 1 µs
18 T1 map.bumper Bumpers, trampolines, bounce walls shape, bounce factor, fixed launch vector or normal, cooldown sim, predictable ≤ 16, ≤ 1 µs
19 T1 map.plate A pressure plate that counts weight (fighters + props) area, threshold, hold ticks sim, a preset of map.zone as map.zone
20 T1 map.portal Teleporter pairs in / out shapes, keep / rotate velocity, cooldown per body, what passes (fighters, props; projectiles in T2), on / off signal sim, host; predictable for the own fighter (pure f(position, state)), with the host confirming ≤ 4 pairs, ≤ 3 µs
21 T1 map.weather A day / night and weather timeline: rain, snow, fog, lightning flashes, gust visuals keys over fight time; the per-map night lock 85-100 (MapLookConf) still applies look; any push it causes is a map.field ≤ 400 shapes
22 T1 map.camrule Camera rules per area or phase: zoom limits, axis lock, look-ahead, a framed "arena" moment areas, limits, ease ticks look (local camera) ≤ 8, negligible
23 T1 map.ambience Positional looping sounds and one-shots tied to signals clip, position / area, volume curve, signal look ≤ 6 voices
24 T2 map.objective Score zones / king-of-the-hill / capture points zone, points per tick, contest rule, moving zone via timeline sim; needs a generic objective rule in the mode system (app code). Ship the hook in T1 if KOTH maps are wanted early ≤ 4, ≤ 3 µs
25 T2 map.light Spot cones, flicker, lights you can shoot out cone, colour, flicker pattern (seeded), breakable look; a shot-out light is a sim bit ≤ 6
26 T2 map.creature A creature rig (shark, kraken arm, drone) on a mover with a bite / hit zone rig, path, attack zone + warning, cooldown sim via mover + hazard ≤ 2
27 T3 map.rope Ropes, ziplines, swings anchors, ride speed needs new fighter movement (gameplay code) n/a

Logic examples the T1 set makes possible, with no new code: a door that opens only while two plates are held (zone ×2 → AND → door); a "last two standing" finale where lava rises and lasers start (alive count ≤ 2 → latch → level + laser); a switch that reverses every belt and spins the rotors the other way (switch → toggle → belt + rotor); a vault that opens after a sequence of three targets is hit in order (switch ×3 → sequence → door → spawner); a seeded random event every 30 s (timer → select(random) → dropper / spikes / field gust).

7.3 Films: new camera, effects, time, sound

All film blocks are look. A film's sim part is only its end pose and the victim's death state, and the gate checks that no new block changes them (§5.7).

# Tier Block What it does Parameters Budget (LOW)
1 T1 cam.path Dolly / crane moves along a spline, with framing that eases toward a target path, speed curve, target (joint / piece / point), roll curve, zoom curve negligible
2 T1 cam.ride The camera rides a round, a thrown weapon or a severed piece (bullet cam for any projectile) subject, offset, lead, roll-with-motion, exit ease negligible
3 T1 cam.handheld Seeded handheld shake profiles (breathing, run, impact) profile, amplitude curve, seed stream negligible
4 T1 cam.snap Crash zoom, snap pan, smash cut with a flash frame target, ticks, overshoot, flash colour negligible
5 T1 cam.freeze Frozen moment with a camera move through it (bullet time in 2D: roll, zoom and slide while the clock is 0) window, camera keys, release ramp ≤ 1 extra full redraw
6 T1 fx.impact 1-3 frame impact frames: two-tone, invert, silhouette, colour-key ticks, style, colours 1 full-screen pass
7 T1 fx.speed Speed lines and radial streaks (the existing RadialBlurFeature on HIGH; shape lines on LOW) centre, density, length, life ≤ 120 shapes / blur on HIGH only
8 T1 fx.ring Shockwave rings, dust rings, ground cracks spreading out centre, radius curve, thickness, colour, count ≤ 40 shapes
9 T1 fx.ghosts Afterimages of a body or weapon at intervals subject, count ≤ 6, gap ticks, alpha fall-off, tint ≤ 6 body redraws
10 T1 fx.transition Ink splash wipe, iris, comic-panel slide, shatter style, ticks, origin 1 overlay
11 T1 fx.screen Screen-space overlays: crack, frost creep, blood-free ink splatter, vignette pulse style, ticks, strength (gore-gated where red) 1 overlay
12 T1 time.stutter Frame-hold patterns (hold 2, play 1) for a beat window, pattern negligible
13 T1 snd.beat Snaps a hit or a stinger onto the music kit's beat / bar cue, kit, grid (beat / bar / half), max shift ticks negligible
14 T1 snd.layers Switches music-kit layers (bed → pulse → drums → riser → hit) on cues kit, layer per cue, crossfade ticks ≤ 4 voices
15 T1 snd.silence A full mute window, then the slam back window, what stays (heartbeat / nothing), slam gain negligible
16 T1 snd.slowmo The slow-motion sound morph as a curve (pitch, low-pass, reverb send) tied to the clock curve shape, floor Hz negligible
17 T2 time.rewind A rewind-and-replay window (the moment again from another camera) window, rewind speed, replay camera ≤ 1 extra decode
18 T2 cam.autocut Auto multicam: cut between named shots on cues and beats shot list, cut rules negligible
19 T2 fx.xray An x-ray window on impact (the old KillCineHud x-ray; anatomy follows the gore level) window, joint, size 1 inset
20 T2 fx.lens Chromatic split, lens dirt, heat haze strength, ticks 1 pass, HIGH only
21 T2 snd.heartbeat A heartbeat bed whose rate follows the clock speed clip, rate curve 1 voice

7.4 Skins and cosmetics: materials, patterns, reactions

All are look, under the no-advantage caps (§7.1). Materials and patterns need one app-shipped feature in Shapes.shader (layers, gradients, pattern functions). That is a shader change in the app, precompiled at build, and packs only set its numbers [user constraint: no shaders in packs].

# Tier Block What it does Parameters Budget (LOW)
1 T1 mat.layers Up to 4 material layers on any body part, weapon part or rig: base, gradient, metallic sheen, rim, outline, emissive per layer: kind, colours, angle, strength, blend +0 shapes, shader ALU only; LOW drops to 2 layers
2 T1 mat.pattern Procedural patterns: stripes, dots, checker, hex, camo noise, cracks, circuit, flames, scales kind, scale, angle, colours (2-3), seed shader ALU; LOW keeps it
3 T1 mat.animate Animates a material: scroll, pulse, hue cycle, shimmer sweep target param, curve, speed negligible; LOW halves the rate
4 T1 cos.anchors+ New attach points: shoulders, hips, feet, weapon muzzle / stock / magazine / grip, orbit points around the head, the ground under the feet (fixed list, app-shipped) negligible
5 T1 cos.react Reaction hooks: on fire, reload, hit dealt, hit taken, K.O., death, idle N s, jump, land, pickup, win, low health event, effect (any look block: fx, pattern pulse, ribbon burst, sound layer), cooldown, cap ≤ 1 reaction live per fighter on LOW
6 T1 cos.emitter Particles attached to an anchor: embers, sparks, snow, petals, smoke, glitter preset, rate cap, life, colour, spread ≤ 24 live per fighter (LOW 8)
7 T1 cos.ribbon Ribbon trails from a joint or weapon part anchor, width curve, life, colours, pattern ≤ 2 ribbons × 32 segments
8 T1 cos.aura Aura layers: rings, glow shells, floating orbiters layers ≤ 3, radius, pulse, orbit count ≤ 40 shapes
9 T2 cos.dangle Look-only physics bits: tails, capes, chains, ear flops (small verlet) anchor, links ≤ 8, stiffness, gravity ≤ 2 chains, ≤ 15 µs
10 T2 mat.flipbook A texture-atlas flipbook on a part (needs a raster bundle) atlas, fps, loop 1 texture ≤ 512²
11 T2 cos.sound An equip sound and a quiet layer on the weapon's fire (never masks gameplay sounds; loudness cap) clips, gain cap 1 voice

7.5 Taunts and emotes: layers, props, FX

The taunt start stays host-decided: Ev.Taunt carries a ContentRef and poses arrive in the snapshots (Taunts.cs:13-15). Props and FX are look.

# Tier Block What it does Parameters Budget (LOW)
1 T1 perf.blend Spring / overshoot blending between keys, mirror, speed scale spring, damping, mirror, speed negligible
2 T1 perf.layer Pose layers: a face / arm / head layer over a base taunt (one base, many variants) base ref, layer keys, weight curve negligible
3 T1 perf.toss Props that are thrown, dropped or kicked, land and stay for a while (look-only physics) rig, launch, bounce, rest ticks ≤ 3 props, ≤ 10 µs
4 T1 perf.fx Confetti, sparkle, emoji bubbles, smoke puff, ink pop, coins preset, count cap, anchor ≤ 40 particles
5 T1 perf.aim Aims a pose or prop at the KO'd body or the nearest opponent (point at, step over, look down) target, joints, limits negligible
6 T1 perf.sting A short music sting with a duck clip, duck level, ticks 1 voice
7 T2 perf.walk An upper-body taunt layer while walking (a rules change: today's taunts need you grounded and free) layer, allowed moves negligible
8 T3 perf.duo Two-player synced taunts partner rule, sync needs a wire change

7.6 Shared blocks

# Tier Block What it does Parameters Online / budget
1 T1 curve Every animatable number: Bézier, the TE eases, step, spring, bounce, seeded noise keys, ease, seed integer keys; negligible
2 T1 rng.stream Deterministic random. Seed = hash(round or film seed, content id, instance, stream id). Draw counts are checked by the gate stream id sim streams only on the host; look streams per phone; never the global Rng
3 T1 cond Conditions: compare a source (fight time, alive count, score, phase, team, gore, tier, platform) with a constant source, op, constant i16 compare; negligible
4 T1 path A shared spline used by movers, cameras, props, ribbons points ≤ 32, kind integer points
5 T1 strings A localised string table per pack (names, signs, words), word-checked by the gate lang → text look
6 T1 palette.set Palette sets, e.g. one per season theme, swapped by the season slots, theme id look
7 T1 variance Per-instance jitter within declared ranges (seeded): no two drops, sparks or spawns identical param, range, stream as rng.stream
8 T1 shape.lib Ready-made shape parts: chain link, flame, spike, gear, star, crack, drip, bolt part, size, colours as shape

7.7 Counts and cost

Family Today's blocks (§5.2) New T1 New T2 New T3
Maps 13 23 3 1
Films 13 (+ common) 16 5 0
Skins / cosmetics 4 8 3 0
Taunts / emotes 7 6 1 1
Shared / common 13 8 0 0
Total ~50 ~61 ~12 ~2

The T1 set is about 35-45 lane-days of the plan, spread over the engines that own each family (§10). T2 adds about 15-20 more.


8. The proof set

The proof set is built in the engine only to prove it can match the originals exactly. Players keep the originals [user]. Each pick covers different blocks, so together they exercise every family in §5.2 before launch.

8.1 What "exact match" means (DECIDED, D7: identical)

The rule (D7: zero difference per frame in shapes, sounds and positions): for every proof item, for every gore level and tier and for a fixed seed, the engine version and the original are run on the same machine at the same gore level. They must produce

Online, each engine proof film and map also passes a host + client run (Mac host, phone client) with matching online hashes (§5.7). studio.sh diff prints all of this per item. It is stricter than the PORT GATE's page parity (2 page units, region SSIM ≥ 0.50), because here both sides are the same game.

8.2 Three maps

Map Why it is in the set Blocks it proves
Backyard (Arenas.cs:214-216) The simplest classic arena and the home arena of most films. It proves the base: exact geometry, one-way planks, spawns + extra spawns, theme, and the film fit checks run on an engine map. map.frame (open, fit-all), map.solid, map.spawns, map.art, map.look, map.elements
Belt Line (Scripts/Level/MapBeltLine.cs) Moving surfaces and a scripted timeline. Two conveyors carry everyone to a central grinder, the belts reverse on a cue, a crate shower falls from reserved props, the timber plank breaks, ladders lead up to steel decks, and the small closed-arena frame is used. map.solid (conveyor), map.hazard (kill zone: grinder), map.timeline, map.event (warning + phases), map.prop (reserved), map.piece (breakable), map.traverse (ladders)
Wrecking Yard (MapWreck.cs) The newest MEDIUM map and the heaviest sim map: follow camera, 2-8 fighters, three pendulum spike balls posed from the swing clock, solid balls, a breakable platform and a 16-brick wall with rebuild, the SMASH event (chain let down), the PALLET event, and an event payload on the wire today written by hand. map.frame (closed, follow), map.hazard (pendulum, solid, HAZARDS off), map.piece (breakable bricks, rebuild), map.event (generic payload vs MapWreck.cs:341-352), state machines, bot nav over pieces

Alternate, if the user wants weight-driven physics in the set: swap Belt Line for Seesaw Pier (a plank tipped by the weight on it, spring + damping + caps, the sea as ring-out).

8.3 Six films

Film Why it is in the set Blocks it proves
SIX-SHOOTER revolver.six-shooter (PageKills/SixShooter.cs, 22 lines, no hooks) The pure-data baseline: if this doesn't match, nothing will. Layered multicam on three rounds, six limb severs at EXTREME, head pop, the gore ladder LOW / MEDIUM / EXTREME from the take. film.take, film.cast, camera (insets / panels), film.cut, film.blood, film.soul, sound
THE X smg.the-x (Scripts/Core/TheX.cs:27-34, wire id 1) The first and reference film. A signature glyph, the single-camera switch, a hat knocked off, and the wall X that stays after the film (drawAfterUnder). text (glyph), camera (single cam), film.prop (hat, outlives the film), film.mark (wall X), film.order (killer over), clock, driver
DRIP revolver.drip (PageKills/Drip.cs:9-23) The heaviest prop film. The real game knife moves between hands and flies with a severed hand along the page arc, an ink-cast gold slab gets a bullet hole, a drip line is left on the floor, brass falls, the track is patched at MEDIUM (no head pop), and voices are dropped. film.weaponprop, film.prop with states, film.cut (piece path), film.mark, film.trackedit, fx, sound edits, counter
HIGH NOON pistol.gen-high-noon (PageKills/GenHighNoon.cs:10-14,33-40) The generic film: one design for every gun of a class (a track per class on a template gun, the real gun drawn and fired on it). This is the "more content, faster" multiplier. film.cast (gun-class binding, trackOf), film.weaponprop (killer's real gun on the template row), film.fit
COLD-BLOODED revolver ICE skin film (PageKills/ColdBlooded.cs:5-21,34,41) A skin's attached film, different at each gore level: frost spreading over the body, a slanted shaft of sun with the killer's projected shadow, a sealed (ice) forearm cut kept on the corpse at EXTREME, a forked tongue and breath fog, dropped words. film.cast (skin slot), look (frost spread, warm / shade tint), grade (light shaft + projected shadow), film.cut (sealed, cap colour), gore variants, rig
THE OFFERED HAND (mythic, PageKills/TheOfferedHand.cs) The weapon-agnostic mythic and the movie replica. Ink swords drawn by the film, a silhouette look on the bodies, a rim / back light, an IK pose fix, and its own suspense music beds. film.cast (any weapon, mythic weight), film.prop (ink-cast props), look (ink silhouette, bodyBack), film.posefix, sound (beds, music kit), rng

THE OFFERED HAND also has a fire variant in its own file (PageKills/TheOfferedHandFire.cs). The proof rebuilds the main one, and the fire variant becomes an engine variant of the same document once the main one matches.

Every film should also prove its GLOAT hand-off (film.handoff) and HIGH NOON should be checked on every gun class whose track ships.

Not covered by these six and left for after launch: a film with a room rule tied to a real wall or ceiling (13 films, e.g. the portal film). film.fit has the surface rule from day one, but its exact match waits for a seventh proof film [call].

8.4 Skins and cosmetics

Item Why Blocks
ICE revolver (premium) The user's own example of a skin carrying sub-features: the look + COLD-BLOODED + ice ammo + ice hit sparks + the ice death burst + the feed icon. cos.item, cos.art (weapon parts), cos.premium (all five attachments), cos.filmrule
A PLAIN weapon skin (one variant, Scripts/Render/CosmeticsPlain.cs) The look-only recolour, the most common skin we will push. cos.art (part recolour), palette
HOODIE outfit (OutfitArt.Hoodie, CosmeticsCatalog.cs:413) A body cosmetic that follows every joint, including in films and ragdolls, plus a hood that sits behind the head (it must behave right when a film knocks the head or hat). It replaces the KURTA: the KURTA is hidden in the game (CosmeticsCatalog.cs:428; the user, 5 Nov: it looks bad, and the game is international). cos.art (body rig on joints + head-anchored part), cos.filmrule
CROWN hat (HatArt.Crown) A head-attached rig that films knock off (PageKill.HatOff). cos.art (head anchor), film interaction
FIRE aura (AuraArt.Fire) An animated layer around the body. cos.aura, mat.animate
RAINBOW trail (TrailArt.Rainbow) A trail that is drawn from the body's history, not its current pose. cos.ribbon

8.5 Taunts and emotes

Item Why Blocks
LOSER (taunt 1) Full-body keyframes, face, words, the L-sticker prop, and its gloat that leaves an L that stays. perf.pose, perf.face, perf.words, perf.prop, perf.clip, perf.gloat (mark)
CHAI BREAK (taunt 10) A gloat that walks to and sits on the body, with the clip timed to the landing (TauntRig.ChaiSitAt). perf.gloat (approach + sit), clip sync
DAB (emote) A hands-only wheel emote, the smallest kind. perf.kind (emote), perf.pose
BHANGRA (win dance) The win-reaction kind. perf.kind (win dance)

8.6 Blocks the proof set doesn't reach

The proof set proves that the engine can match the originals exactly. It can't reach every block. These are covered by a block test suite at the gate instead: one small scenario per block, run like the proof, that checks the block's own behaviour, determinism and budget.

For a block that does have a built-in user (e.g. Seesaw Pier for weight-driven pivots), the test compares against that map's own scenario.


9. Risks

Risk What could happen What we do
Expressiveness gap The custom code in 146 film scripts does things no block can say, and new films keep needing "just one hook". The proof set covers the heaviest scripts (DRIP, COLD-BLOODED, THE OFFERED HAND). At the start of Stage 3 an agent tags every film script's hook body against the block list, and the gaps become blocks or parameters. We accept that some films will wait for an app update with a new block. That is the price of no scripting.
"Exact" is hard Draw-order and float differences between hand code and blocks give tiny frame differences. Wrapping (not rewriting) the existing code paths, comparing draw lists after quantisation, a per-item diff report, and a decision on how strict exact is (§8.1).
Store review A reviewer reads "downloads animations" as code download. No scripting by design (§5.15), bundle checks, clear review notes. A first submission with the engine shipping but only built-in content live, then pushes after approval [call].
Determinism across platforms IL2CPP on ARM vs Mono / IL2CPP on x64 differs in float maths in sim blocks. Replicated state is host-only and sent quantised, so phones never have to agree on float maths. Only gore-independent film state is recomputed and hashed, and it is integer or quantised first (§5.7 rule 3). Golden runs at the gate on Mac, Linux and the reference phones (ARM).
New blocks have no original The ~61 new blocks (§7) can't be "matched", so their bugs are only found by their own tests. The block test suite (§8.6), the gate's determinism runs, and a first wave of small internal-only packs that use each new block before any live item does.
Store review turnaround A rejection, or a long review of the first build with the engine, moves launch. Submit the engine build early with only built-in content live. Keep the review notes ready, and budget a resubmission week in the calendar.
Join friction Fetching a missing pack before a join makes friends wait. Small vector packs, prefetch at app start, packs for a channel kept to a modest set. The room uses the host's set, and the joiner's progress is shown to the room.
Download / storage Sounds grow the cache, old phones are short on space. Size caps, LRU eviction of unused packs, sounds shared through library packs.
Performance on low phones A heavy pack runs fine on the Mac and stutters on a LOW phone. Per-block costs and per-tier budgets at compile time, phone-tier checks before live, the runtime budget meter.
Studio scope creep A full visual editor delays launch for months. A minimal Studio v1 (§11) plus a CLI-first build order. The import of baked takes keeps the kill-page pipeline as the motion source [ask].
Two catalogues drift Built-in and engine items behave differently in the shop or pass. One ContentCatalog interface, with built-ins adapted rather than duplicated, and one export.
Formulas (D12) and app review A reviewer reads downloaded formulas as code. The formula set is tiny and bounded (§5.5a), the review notes describe it, and there is one server kill switch to constants, which every item is gated with.
A bad pack reaches players A crash or an offensive string. Gate + approval + staged rollout + kill list via config, plus the phone's validator dropping invalid docs whole. Rollback is a config version.
Signing key The publish key leaks and someone pushes a pack. The private key lives only in the gate's secret store on the VM (not the repo, not the Mac). Rotation by an app update carrying two public keys.
Load on our machines The gate's every-arena × every-level × every-tier runs are heavy. Gate runs on the VM / M2 by the existing lane rules (Mac last, load bands), and per-item caching of unchanged variants.

10. Build order and sizes (sequential engines)

The user (6 Nov): "u build one by one, first map engine, then animation engine, like that" [user]. Each engine is finished end to end, with its blocks, its proof items identical, its gates, its online play, its Studio editing (agent-first, then visual) and its playbook, before the next one starts. The full task list is docs/superpowers/plans/2026-11-06-content-engine.md.

Stage What Build (lane-days)
1 Shared core (only what maps need) 3 map de-risk spikes; document model + schema generator; pack format + hashes + store; loader / validator / budget meter; logic graph; formula set (map slots); seeded streams; agent CLI; sbapi registry + content / kill lists + approval; content.stickbash.com; splash download 36-42
2 MAP engine today's map blocks + the 23 new map blocks + shared T1 blocks; bots; protocol 145 + relay + online pack checks; bundle lane; proof maps identical; map gate; Studio map editing; playbook. Done: an agent makes a brand-new map from the CLI, previews it, bot-tests it, passes the gates, publishes it to test phones, and it plays online 50-60
3 ANIMATION engine the film runtime spike; film blocks on the page-film engine; the 16 new film blocks; the kill-page → Studio import; 3 gore levels; protocol 146; the 6 proof films identical; Studio film timeline 42-52
4 SKIN engine the shader material feature; cosmetic + the 8 new skin blocks; premium attachments; loadout extension (147); owned-item fallback; the 6 proof cosmetics identical 22-27
5 TAUNT / EMOTE engine performance + the 6 new taunt blocks; taunt ref (148); the 4 proof performances identical 12-15
6 Catalogue + polish + launch the shared catalogue across shop / pass / customize; Studio polish; reference-phone step; store submission; drills; the launch rehearsal 12-16

Total: about 174-212 lane-days (about 200-243 with review rounds). That is about 10 % more than building everything interleaved, the price of finishing one engine at a time.

Calendar:

Lanes Map engine done Launch-ready
1 lane ≈ 14-17 weeks ≈ 30-36 weeks
2 lanes ≈ 9-11 weeks ≈ 19-23 weeks
3 lanes ≈ 8-9 weeks ≈ 16-19 weeks

Protocol: each engine adds its wire messages with one bump (145 maps, 146 films, 147 skins, 148 taunts). This is allowed only because no player builds exist before launch, and the launch build carries the last number [call].


11. What launch needs

Launch waits for the engine, the Studio and the proof set [user]. Concretely:

  1. All four engines (Stages 1-5, each passing its own done definition) in the store build: ENGINE_API = 1, every block family of §5.2 plus the ~61 T1 new blocks (§7, D1), the final protocol number, and the shared catalogue serving built-ins and packs (Stage 6).
  2. The proof set exact (§8.1 report green): 3 maps, 6 films, 6 cosmetics, 4 performances, all on the internal channel only (players keep the originals). Every new block also passes its block test (§8.6).
  3. The agent interface (Stage 1, grown in each stage; §6) on the VM / M2 / Mac, meeting the < 30 s edit → still target, and the Studio v1 (each engine's editing + the Stage 6 polish) on the Mac. At least one item of each kind is made end-to-end by an agent following the playbook, through gate → approval → the test channel.
  4. The pipeline live: sbapi content, content.stickbash.com, the content list and kill list, signing, human-only approval, rollout, and a rollback + cascade drill done.
  5. Store: review notes written, the first submission accepted with the engine in it.
  6. At least one real new item (a skin or a taunt) pushed to test and then live after launch day, to prove "no app update" in production [call].

Not needed for launch: migration of any old content, player creation, the seventh (room rule) proof film, a second CDN origin.


12. Left to the builder [call]


DECISIONS FOR THE USER (all DECIDED)

The numbering follows the coordinator's asking order (D1 … D15). All 15 were decided on 6 Nov; the design is APPROVED.

  1. How many new building blocks go into the launch app? Pushed items can only use blocks the app already has. A block we leave out can't be used until the next app update.

    • DECIDED (6 Nov): (A), today's blocks plus the ~61 T1 new blocks.
    • (A) Recommended: today's blocks plus about 60 new ones (§7, the T1 list). That is about 23 for maps (moving and spinning platforms, switches and doors, portals, lasers, saws, crushers, spikes, falling objects, spawners, rising water and lava, wind and gravity zones, weather, and the wiring that combines them), 16 for films, 8 for skins, 6 for taunts and 8 shared. Launch is about 10-13 weeks away.
    • (B) Everything in (A) plus the next 12 (T2: score zones / king of the hill, creatures, x-ray and lens effects, dangling capes and tails, walking taunts and more). Launch moves about 3-4 weeks later.
    • (C) A smaller start: today's blocks plus about 5 new ones per kind (around 25). Launch is about 2-3 weeks sooner, and more new ideas wait for app updates.
    • (D) Today's blocks only. Launch is about 8-10 weeks away, and anything new needs an app update.
  2. Can an item made by an AI helper reach players without a person approving it? Every item passes all the automatic checks first.

    • DECIDED (6 Nov): (A), a person (you or the lead coordinator) approves, and the server enforces it.
    • (A) Recommended: a person (you, or the lead AI coordinator on your behalf) looks at the item's check sheet and taps APPROVE before players get it. Helpers may send items to our own test phones on their own. The server enforces this: only a person's dashboard login can approve.
    • (B) Passing the checks is enough. Helpers publish to all players themselves.
    • (C) Only you approve, item by item.
  3. How complete must the Studio (our Mac creator app) be at launch?

    • DECIDED (6 Nov): (A) at launch. Freehand drawing and posing come in a later Studio update, with no data change (§6.7). The user: AI agents will build ~95 % of content, so the agent interface comes first (§6).
    • (A) Recommended: it works for all four kinds (maps, films, skins, taunts). You can open an item, try it in a real match against a bot, change any setting, set poses and timing on a timeline, wire map logic, run the checks and publish. No freehand drawing tools yet: shapes are typed in or imported.
    • (B) Everything in (A) plus freehand drawing for shapes and a drag-the-stickman posing tool. Launch moves about 2-3 weeks later.
    • (C) Only the helpers' typed commands and a preview window at launch. The visual editor comes later.
  4. A friend joins an online room that uses a new map or film they don't have yet. What happens?

    • DECIDED (6 Nov): (A) + (C). The app downloads new content at the splash with a progress bar before the menu, continues with cached content if offline, and rooms download a missing pack with a progress bar before joining (§5.13).
    • (A) Recommended: their phone downloads it first, then they join. A progress bar shows the wait: a second or two for most items, longer for an item with new sounds.
    • (B) The room only picks maps and films everyone already has, so nobody waits.
    • (C) They can't join until they download it themselves.

    (Their friends' new outfits are never a problem: they download in the background, and a plain default look shows until then.)

  5. When does a phone download new items?

    • DECIDED (6 Nov), settled by D4: at app open, on any network, blocking with a progress bar. The size cap of D6 applies.
    • (A) Recommended: quietly in the background when the game opens, on any network. Most items are tiny because the art is drawn by the game itself, not pictures.
    • (B) Only on Wi-Fi.
    • (C) Only when the player opens that item or joins a room that needs it.
  6. How much space may downloaded items take on a phone?

    • DECIDED (6 Nov): (A), at most 5 MB per pack and 150 MB in total, with LRU eviction; owned items are always kept.
    • (A) Recommended: at most 5 MB per pack and 150 MB in total. Packs not used for a while are deleted and downloaded again if needed. Items a player owns are always kept.
    • (B) Tighter: 2 MB per pack, 60 MB in total.
    • (C) No limit.
  7. How exact is "exact" for the proof set?

    • DECIDED (6 Nov): (A), identical: zero difference per frame in shapes, sounds and positions.
    • (A) Recommended: identical. The computer compares every frame's shapes, sounds and positions with the original and finds no difference.
    • (B) No difference you can see side by side. Tiny invisible differences are allowed.
    • (C) Same look and feel. Small visible differences are allowed.
  8. Are these the right proof items? Maps: Backyard, Belt Line, Wrecking Yard. Films: SIX-SHOOTER, THE X, DRIP, HIGH NOON, COLD-BLOODED, THE OFFERED HAND. Cosmetics: the ICE revolver, one PLAIN skin, the HOODIE outfit, the CROWN hat, the FIRE aura, the RAINBOW trail. Taunts: LOSER, CHAI BREAK, DAB, BHANGRA.

    • DECIDED (6 Nov): (A), as listed, with the HOODIE.
    • (A) Recommended: yes, as listed.
    • (B) Yes, but swap Belt Line for Seesaw Pier (it tests a plank that tips with players' weight).
    • (C) Add more films (8-10) for wider coverage. Launch moves about a week later.
  9. How do new items reach players?

    • DECIDED (6 Nov): (A), our test phones → 10 % for a day → everyone, with a one-tap pull-back at every step.
    • (A) Recommended: our own phones first, then 10 % of players for a day, then everyone. Any step can be pulled back in one tap.
    • (B) Our own phones first, then everyone.
    • (C) Everyone at once.
  10. Where does the movement of new kill films come from?

    • DECIDED (6 Nov): (A), motion from the kill pages; the Studio adds props, camera, sound and the gore levels.
    • (A) Recommended: as now, from the kill pages we already make in the browser, brought into the Studio. The Studio adds props, camera, sound and the gore levels.
    • (B) Posed directly in the Studio from the start.
    • (C) Both from day one. More to build before launch.
  11. Must every new kill film be made at all three gore levels?

    • DECIDED (6 Nov): (A), all three gore levels authored.
    • (A) Recommended: yes. The maker builds LOW, MEDIUM and EXTREME (as today), and the checks play each one.
    • (B) The maker builds EXTREME only, and the game works out MEDIUM and LOW automatically. Faster, but less control over how the lower levels look.
  12. Should content be allowed small formulas? Some effects would be quicker to describe with a formula, for example "glow gets brighter the faster you move".

    • DECIDED (6 Nov): (B), the user overrode the recommendation: a tiny bounded formula set (§5.5a) with a server kill switch to constants.
    • (A) Recommended: no formulas. Only the fixed blocks, curves and wiring. This is the safest reading of the app-store rules, and a missing ability becomes a new block in an app update.
    • (B) Allow a tiny fixed formula set (add, multiply, min, max, no loops). More flexible, with a small app-review risk.
  13. We pull a broken item that players have bought. What do they keep?

    • DECIDED (6 Nov): (A), they keep it: it rolls back to the previous good version, or is hidden until the fix if there is none, then returns by itself.
    • (A) Recommended: they keep it. It goes back to its previous good version, or is hidden until the fix if it had none, then comes back by itself.
    • (B) It is hidden until the fix (still owned), even if an older good version exists.
    • (C) We refund their gems as well.
  14. Where are the download files kept for players?

    • DECIDED (6 Nov, the coordinator's technical call): (A), our server behind the Cloudflare cache, immutable files. The setup still to do is listed in §5.13.
    • (A) Recommended: on our own server, behind Cloudflare's worldwide cache. Files never change once published, so the cache serves almost every download. Low cost, nothing new to set up.
    • (B) Cloudflare R2, a paid storage service. Our server does no work for downloads, at a small monthly cost per use.
    • (C) Google's storage (Firebase). Works well, but it is a separate bill and another account to manage.
  15. Where do the designs and sounds we make live?

    • DECIDED (6 Nov, the coordinator's technical call): (A), a new private repo stickbash-content.
    • (A) Recommended: in their own new private project on GitHub ("stickbash-content"), apart from the game and server code. Helpers can make content without any risk of touching code.
    • (B) Inside the server's project.
    • (C) Inside the game's project.