1.0.1 — work queue
1.0.1 — work queue
Section titled “1.0.1 — work queue”CLOSED 2026-10-14: iOS + tvOS 1.0.1 (store 255) approved and live. The next store version is 1.1; its queue is
docs/RELEASE-1.1.md. Move items from here to there, do not tick them here.
1.0 (store build 37 = Sim_Const.BUILD 206, commit 56e1d13) went to App Store review on
2026-09-28. v0.1 became 1.0, so docs/RELEASE-0.1.md is closed as a work queue; this file is
the queue for the next store version. Anything anyone finds goes here first; tick items
✅ DONE (commit) in place.
- Version string is
1.0.1, not1.01— Apple compares dot-separated integers, so1.01==1.1. - Build numbering (Chris, 2026-09-28): one number everywhere. The next store upload uses
store build =
Sim_Const.BUILD(so 207, not 38);scripts/release/release.shdoes this now. - Dev / beta / store pipeline — ✅ DONE (build-channels branch; Worker deploy + D1 migration
- device install pending Chris) — spec
docs/superpowers/specs/2026-09-28-build-channels-pipeline-design.md(separate Dev app, unified numbering,store-Ntags,channelfield, dev flags out ofmain.gd). ⚠️ Deploy order: apply the 10ALTER TABLEs (telemetry/schema.sql, 2026-09-28 block) to live D1 first, verify withPRAGMA table_info(<t>)on all 5 tables (samples, runs, encounters, feedback, hitches), THENwrangler deploy. Worker-before-migration makes every INSERT fail (no such column) and drops live 1.0 telemetry.
- device install pending Chris) — spec
Status: SUBMITTED 2026-10-04 (store build 255, iOS + tvOS, WAITING_FOR_REVIEW)
Section titled “Status: SUBMITTED 2026-10-04 (store build 255, iOS + tvOS, WAITING_FOR_REVIEW)”Carry into 1.0.2 (the iOS listing went in half-updated, Chris chose not to pull it again):
- Upload
marketing/appstore/ipad_v2/(8 screenshots) to the iOS iPad set; iOS 1.0.1 still shows the 6 old 24-Sep shots. - Upload
marketing/appstore/previews_v2/iphone/+previews_v2/ipad/(gitignored; rebuild commands inmarketing/appstore/SUBMIT.md). iOS 1.0.1 has no previews. tvOS 1.0.1 has all 3. - Write the iOS What’s New (1.0.1 was the first iOS release, so it had none).
1 · Bugs (fix first)
Section titled “1 · Bugs (fix first)”-
✅ DONE (fb/stats-sheet-fit; adaptive crystal row pitch with a legibility floor, right-stick scroll + d-pad follow, preview-changed rows sort first, rung labels clear of counts, stale Pickup stat removed; fit test over 5 loadouts x 3 devices x both hosts; needs Chris’s ATV check with a pad) Stats sheet on tvOS: text clipped at the box edges, scroll columns don’t scroll, text too big for its boxes (Chris voice feedback, D1, 2026-10-11, BUILD 297 ATV). This is the TV-legibility verdict owed on the
stats-sheet-navmerge (4a4a3cd3), and it fails.ui/stats_sheet.gd. Fix + a fit test driven at the TV (1280) AND iPad (985) / iPhone (1204)ShopLayoutFixtureprofiles, asserting text rects stay inside their boxes and the columns scroll by focus/d-pad. -
✅ DONE (fb/knockback-clamp; knockback can no longer carry an enemy past ±ARENA_HALF in survival, story excluded because its rooms exceed the arena; still unclamped: enemies idling/kiting 0-250 px past the edge before the leash) Rift Bolt knockback pushes enemies outside the arena (Chris voice feedback, D1, 2026-10-11, BUILD 297 ATV).
sim/weapon_aim.gdDEF_KNOCKBACK260 + theknockbackmod. Clamp knocked-back enemies to the arena bounds (determinism-sensitive: sim change). -
BLOCKS THE NEXT STORE UPLOAD: controller telemetry is a new KIND of data; privacy page + ASC label not updated (2026-10-07 perf/controller workflow).
PadLivenessnow sendspad_conn/input_gap/input_resume(rough pad type, slot, counts; never the Bluetooth name) andmarketing/appstore/compliance.mdwidened “Other Diagnostic Data” to all platforms. Still to do: the public privacy page in~/Claude/bullet-heaven-site/privacy/(separate repo, deploy via itsscripts/deploy-site.sh) and re-tick the ASC label for tvOS/macOS. (2026-10-11: the ASC App Privacy label is per APP, not per platform, and “Other Diagnostic Data” was already ticked for MetricKit, so the label likely needs no change, only Chris confirming the tick in ASC > App Privacy; ✅ privacy page DONE 2026-10-11: “Game controllers” bullet live, site commit bcea2eb, verified on darkcosmos.lumara.digital/privacy/.) 📍 Only left: Chris confirms the Other Diagnostic Data tick in ASC > App Privacy. Merge is fine; the store upload is not until both are done. -
Verify on device: the forced-tutorial boot prewarm + 6 new PrewarmPass families (merged 2026-10-07). Fresh Dev install with the tutorial unfinished on the ATV and iPhone, then
SELECT build,dev,frame_ms,pipelines,run_time FROM hitches WHERE pipelines>0 AND run_time<1 AND build>=Nmust return zero rows (it was 2.0-3.3 s / 12-13 pipelines on 268/271). Also eyeball the boot cover (“DARK COSMOS” on a NeonBackdrop + GlassPanel) and the start-menu stutter the extra families may add (ScreenFeedback 65k set_pixel vignette + full ArchetypeRenderer now build as the menu appears, unmeasured). Then skillbh-fresh-install-stall-check: fresh-install stall seconds were 7.9 s on 260/262/264 and the metallib bake changed nothing, cause not yet found. Findings:docs/research/2026-10-07-perf-and-controller-workflow-audit.md. -
BLOCKS THE NEXT STORE UPLOAD containing the HDR/SDR colour-profile work: on-device checks not yet run (2026-10-09, hdr-colour-spec final review I1). (a) iPhone: HDR engages and the active colour profile follows it (plan Task 8,
docs/superpowers/plans/2026-10-09-hdr-sdr-colour-systems-step1.md). (b) Fresh-install shader-stall check (skillbh-fresh-install-stall-check) on Apple TV AND iPhone: the newborder_haloglobal shader uniform (declared in[shader_globals]) was never verified through the export shader baker. -
Controller “stops responding”: repro + read the new telemetry (2026-10-07). Two 30-min Nimbus sessions on the ATV on the next Dev build, noting wall-clock times of each dead moment, then join against
eventstypespad_conn/input_gap/backgroundandhitches(skillbh-telemetry). Already shipped: auto-pause when the app resigns active mid-fight (D1 showed 2-25 s frozenbackground/foregroundpairs in live play, one 24 s on 273, then the player dropped straight back in). Still open from the audit: Survival runs start in attract autopilot and ignore RB/LB until the stick moves (round 2 in flight); co-op pilot<->pad mapping is connection order so a reconnect swaps/orphans pilots;GCSupportsControllerUserInteraction=truein the tvOS plist as a possible overlay trigger (unverified). -
Webs (Scar Tissue v2) are a measured fps cost on the ATV at equal enemy counts (BUILD 273). Auto session 9874f3: webs 0 -> 59.6 fps, 1-49 -> 56.7, >=50 (avg 149) -> 48.2; human 142ad2 webs >=50 (avg 66) -> 53.8. Isolate per web with
tools/perf_probe/look_bench.gdon device, then cull off-screen webs / merge into the web MultiMesh layer / cheaper tier at webs>=50. Gate: an ATV session with webs>=50 holds >=58 fps. ✅ DONE in code (fix2/webs-render-cost merged 2026-10-08, 815d4d29/01d7150a): at 50+ webs on screen the web look drops to end rings + ring-weight side lines (pixel-identical below 50; M5 GPU 0.76x at 150 webs, 0.53x at 400), A/B flagwebs_litein Debug Settings + console, threshold/hysteresis tunables (401 = never), PrewarmPass family 26. Still to verify on the ATV: D1samples WHERE build >= <first build with it> AND webs >= 50, expect avg fps >= 58; and Chris eyeballs the no-fill corridor look on TV. -
Mid-run 0.9-1.3 s stalls on the ATV are NOT script time (
proc_ms6-90 ms vsframe_ms~1000; tight 940-1050 ms cluster at ~200 enemies on 262/264, ~1 per hour since 268; looks like a MetalnextDrawabletimeout, not shader compile). Needs drawable-acquire / GPU time in the fork’s hitch payload to split GPU-bound from present-blocked. The GRAVITON / BOSS DOWN ~1 s rows are NOT explained by the unwarmed screen passes (skeptic-refuted); probe pipeline counts before adding families. -
✅ DONE (verified 2026-10-11:
deploy.sh --check-onlygreen, Worker deployed 2026-10-09 09:03 UTC after the lasttelemetry/srccommit 96c139e3, livesamplesrows carry frozen_reason gameover/levelup/pause/…) Frozen-state telemetry needs its 5 D1 ALTERs applied, THENtelemetry/deploy.sh(Chris’s OK; production). Migrationtelemetry/migrations/2026-10-07-frozen-state.sql. Worker-before-migration makes every sample INSERT fail. Until deployed, the newfrozen/reason fields are dropped by the old Worker (harmless). -
✅ DONE (lan-jitter) LAN co-op client stalls often over Wi-Fi: the jitter buffer in the spec was never built (2026-10-07, Dev 271, Mac client → ATV host). Chris: “worked ok but quite a lot of stalls”. A headless Mac client over the same Wi-Fi stalled 108 of 600 frames (~18%); the Mac → ATV ping is ~17 ms, typical Wi-Fi clumping. Cause:
LockstepSession.step()(client) ticks the moment ONE row is queued and only catches up (2 ticks/frame) aboveCATCHUP_QUEUE3, so every late packet is a visible freeze. Specdocs/superpowers/specs/2026-10-07-networked-coop-design.md§4 “Client tick pacing” says the client keeps a 1–2 row jitter buffer. Fix: prefill ~2-3 rows before the first tick, aim to hold that cushion (catch up above it, wait only below it), test it with LoopbackTransport injected jitter (stall count must drop), and reportstallsin telemetry so device runs can be compared. No netcode library: netfox / godot-rollback-netcode were evaluated in that spec §6 and rejected (they assume Node-property state, not our PackedArray sim). -
iPhone JOIN list stays empty while the Mac lists the ATV (2026-10-07, Dev 271). The ATV registered fine (code VGE3Y, net key = this Wi-Fi’s public IPv4, no IPv6 on the network). The Mac also hears the UDP beacon, so its row does not prove the registry path; iOS only gets the registry. Check: is the iPhone on the same Wi-Fi (not cellular)? Does typing the code join? If yes, debug
/mp/nearbyfrom iOS (LanRegistry.start_nearby). -
✅ DONE (20782220) LAN join on iOS fails as a bare “Could not join: host_timeout” when Local Network access is off (2026-10-07, Dev 271). iPhone → ATV host timed out on every path; the host was registered (code ESBEW,
192.168.68.123reachable from the Mac, a headless Mac--mp-joinconnected) and the join worked as soon as Chris enabled Settings → Privacy & Security → Local Network → Dark Cosmos Dev. iOS silently drops the UDP, so the player gets a timeout with no hint. Fix: on an iOS clienthost_timeout(all IPs tried), show “Allow Local Network for Dark Cosmos in Settings → Privacy & Security” in the lobby status, and consider a pre-join local-network probe to trigger the system prompt. Also seen: the ATV advertises a dead second IP (10.198.61.241), which costs one extra timeout before the real one whenorder_ipsranks it first. -
✅ DONE (521fdee2) Button prompts show Xbox colours on non-Xbox pads (Chris, 2026-10-04, SteelSeries Nimbus: “the colours dont match the controller”).
ButtonPrompt.style_for_name()only recognises PlayStation names; every other pad falls through to"xbox"(green A / red B / blue X / yellow Y discs). Fix: match Xbox by name, and give every other pad (Nimbus/MFi, 8BitDo, Switch Pro, unknown) a neutral style, with the same letters on a plain disc in UI tokens. Device check needed: the exactInput.get_joy_name()string for the Nimbus on tvOS/iOS/macOS. Fixed: Xbox matched by “xbox”/“microsoft”/“xinput”, every other pad gets the new"neutral"style (token disc, accent rim, L1/R1 bumpers). Pad name NOT confirmed on device: the engine fork’s SDL MFi driver names an Apple pad byGCController.productCategory, which should be “MFi” for a Nimbus (the SDL DB names are “SteelSeries Nimbus”/“SteelSeries Nimbus Plus”); all of these fall to neutral. Still pending: Chris confirms on the Nimbus. -
iPhone (iPhone16,2) Dev BUILD 251: Button labels blank AND player hull invisible (2026-10-03). ✅ DONE (72c55003, branch
ios-baker-saved-materials): fix = no runtime-created CanvasItemMaterials. All 41CanvasItemMaterial.new()+BLEND_MODE_ADDsites (render/, fx/, ui/, main.gd) now preload the shared savedres://fx/additive_mat.tres;tests/test_baked_shader_materials.gdbans new ones. Baked (baker ON) iPhone Dev BUILD 254: hull draws in Lesson 1 (screenshot) and Button labels draw (Chris confirmed). The 23 runtime.duplicate()s of saved ShaderMaterials did NOT need changing. Store iOS 1.0 (build 37) still has the bug on A17+ iPhones; 1.0.1 carries the fix. ROOT CAUSE FOUND (BUILD 253 experiment): the iOS shader baker. An iOS-Dev export withshader_baker/enabled=falsedraws the hull AND the Button labels on the iPhone (screenshot + Chris confirmed). Matches upstream godotengine/godot#124088 (4.7.2/4.8.dev, iPhone 17: “argument canvas_data[0] has space for 160 bytes, but argument has length(272)” Metal validation error, failed draw calls, UI text missing; workaround: no runtime-created materials; no fix PR yet). iPad M1 unaffected. The store iOS 1.0 (build 37) and every baked iOS build likely have it on A17+-class iPhones. Fix options: (a) baker off for iOS presets (accept first-use stalls); (b) replace every runtimeCanvasItemMaterial.new()/ShaderMaterial.new()with saved .tres (the hull glow sprites still build theirs in code); (c) fork runtime + templates for iOS like tvOS. Chris to decide. Screenshot (devicectl device capture screenshot, works over USB): pause menu plates draw, every Button label is blank; Labels (PAUSED, TIME, “Echo”) draw, same Orbitron font as the buttons. Ship: nothing drawn at all. Chris: “i think this happened before” on iPhone. ATV (fork runtime) last seen fine; Mac with the SAME stock 4.7.1 +--rendering-method mobile+platform/tvosproject renders both fine, so it’s iPhone/stock-iOS-runtime specific. Ruled out: missing assets (ship3d_*.ctex and fonts are inbuild-ios/BulletHeaven.pck), pack/engine mismatch (both 4.7.1.stable.official). Toggling Debug Settings “Liquid-glass UI” + “Nova Look” off did NOT fix it (pause still showed glass plates after, so confirm the toggle actually applied). Godot print output does NOT reachidevicesyslogon this build — add file logging or an on-screen error overlay to get evidence. Next: check the iPad (same stock pipeline) to split device-GPU vs. iOS-pipeline; check whether a fresh full--export-release "iOS-Dev"(new xcframework, last full export 2026-09-29) changes it. Update BUILD 252: Dev builds now writeDocuments/logs/godot.log(pull withdevicectl device copy from --source Documents/logs/godot.log); iPhone + iPad logs show NO render/load errors. iPad (M1, tutorial already done) start menu renders all text fine; iPhone (A17 Pro, fresh save → Tutorial) still shows no hull. Tutorial on the iPad shows the ship (Chris, 2026-10-03), so it is NOT the Tutorial path: it’s iPhone-specific. Remaining differences: GPU (A17 Pro / Apple9 vs M1 / Apple7), content scale (3x vs 2x), aspect (19.5:9). Next: does the hull show in Survival on the iPhone; try a Mac run at the iPhone’s resolution/scale (--resolution 2796x1290); check Godot 4.7.1 issues for Apple9/A17 canvas regressions. -
PSO prewarm was culled whole: Warden v2 first-link stalls on BUILD 248 ATV (2026-10-02). ✅ DONE (7246ebbf, branch
prewarm-cull-fix). Six 0.5-1.1 s stalls at run_time 214-237 s were the Warden v2 looks (swing, lane, bolt, charge, drone, burst, missile) first-linking mid-fight. Root cause:render/prewarm_pass.gdhid itself with modulate alpha 0.001, but Godot’s canvas cull skips any item whose combined alpha is < 0.007 (renderer_canvas_cull.cpp), so nothing outside SubViewports was ever submitted; the TouchControls prewarm pad (alpha 0.001) likewise. Fix: the pass draws at full alpha under the start menu’s opaque backdrop and hides + frees the instant the menu hides or leaves the tree. Desktop probe (mobile renderer): menu prewarm 9 → 38 pipelines; Warden fight 10 links (all 7fx/boss/shaders) → 1-5, none Warden; menu pixel diff with/without the pass = 0 (mobile + GL compat).HitchTrackeralso keeps freshnew:tags over repeat-tag spam. Guardtests/test_prewarm_cull_alpha.gd. Open: ATV fresh-process Warden fight should show nonew:-tagged boss stalls; recheck the iPad run-start rows (the BUILD 220 “pad families not missing” verdict was taken while the pad was culled); the probe still shows ~10-14 links during ordinary play after the menu (e.g.enemy_smear), a separate prewarm-coverage gap. -
BUILD 243 Apple TV GPU page fault with an evolved Wildfire / Firestorm chain (2026-10-01). AppleTV11,1 (A12, native Metal fork) faulted (“BIF0 page fault”, is_read, fragment work) ~4 s after the chain pushed weapon zones past 20. Prime suspect (by elimination vs BUILD 240, not reproduced on Mac Metal with validation): the
perf_firestorm_clusterper-zone neighbour lists from 9d1c3c92, a dynamically indexeduniform vec4 nbrs[256]inrender/weapon_zone_field.gdshader, live above 20 zones. ✅ FIX LANDED (BUILD 244, branchfx-v2-promote-railgun):nbrs,WZ_NBR_THRESHOLD/WZ_NBR_Kandweapon_zone_neighboursremoved; only the lossless early stop remains (early_stopuniform bool), andPerfCuts.firestorm_clusternow defaults OFF (row kept for an on-device A/B). Open: Chris to confirm on the Apple TV that an evolved Wildfire chain past 20 zones no longer faults with the row OFF, then again with it ON. -
Orbit shards / blaster bolts randomly vanish mid-run (Chris, 2026-10-01). ✅ DONE (7fce5123). Engine-level, not the shaders:
MultiMesh.buffer = ...never emitschanged(unlikeset_instance_transform_2d), so a buffer-fedMultiMeshInstance2Dnever redraws and canvas culling tests the rect cached at its LAST redraw. Once the player moved away from that spot the whole look was culled, popping back on the next visibility toggle. Hit every weapon FX v2 look (persistent orbit/bolts worst, since they never toggle) plus ZoneRenderer’s web + zone layers. Fix:MultiMeshCulling.disable()(all-covering custom AABB) on all four builders; repro’d and verified on mobile + gl_compatibility;tests/test_batched_layer_culling.gdalso guards any future.buffer =layer. Needs an on-device eyeball (ATV: orbit + blaster across a long run). -
Halo Chain enemies pay 0 XP. ✅ DONE (2026-09-30). A full audit of every
enemies.add()call site found THREE truncation bugs, not one: Halo Chain packs (halo_chain_director.gd— 11 args), Splitter children (sim.gd_sweep_dead— 10 args) and Boss2’s summon adds (boss2.gdfire()— 10 args), all leavingbiomassat its 0 default while the bible values landed in the never-readxp_valcolumn. Same trap as the 2026-08-19 story-mode bug. All three now pass the real biomass (Splitter children and Boss2 summons pay the swarmer’s bible biomass, mirroring_spawn_elite_swarmer_brood’s unit-biomass pattern; the summon spawn was extracted intoBoss2.spawn_summonsso the real path is testable). Bosses (flat 400 fallback), tank missiles and FunZo jesters are intentionally 0-biomass — documented intests/test_spawn_xp.gd. XP asserted through the real spawn →damage_enemy→_sweep_deadpath (tests/test_spawn_xp.gd). No determinism re-pin: all three baselines held (halo packs spawn past the 10s window, and no splitter died in it). -
Weapon evolve rungs are unreachable in human runs (Chris, 2026-09-30: “why don’t the weapons ever seem to become fully evolved”). In Survival, evolutions fire ONLY from
WeaponThresholdsrungs (level-upevolve:cards are crystals-era residue, never offered there) — and the rungs need 15-30 crystals of ONE element (mostly 18; aim 30). Crystals only arrive via the one grant card per level-up, 1-3 of an element each. Live D1 data (40 recent crystal-picking runs, 2026-09-30): best single-element total in any HUMAN run = 12 (a 7.5-min run; most top out at 3-10); only 6-20-minute attract/soak runs (void 20-31) ever cross a rung. The values are markedSTARTER VALUES — balance via telemetryinsim/weapon_thresholds.gd, and that balance pass never ran. PARKED (Chris, 2026-09-30) for a dedicated balance pass — no spot-fixing the TABLE without it. (Compounding factor: on today’s main an evolved weapon barely LOOKS different — Blade Tempest still draws a ~97° crescent (fixed by the FX v2 batch 1 merge in §3b) and Beam Lance ignores width. Beam Lance’s width is fixed by FX v2 batch 2 (branch fx-v2-batch2-beam-railgun), so the payoff will at least be visible once those land.) -
Remote config offers boss stats nothing reads.
remote-config/manifest/build-206.jsonlistsenemies.funzo/warden/graviton.{hp,armor,speed,contact_damage}, but those bosses spawn from code consts (FUNZO_HP…); only the Eye reads the bible. A patch would publish and do nothing. Either exclude them from the manifest or make the bosses read the bible. ✅ DONE (c142f22f): all four fields now reach the real pooled spawn path. Warden’s bible survival defaults now match its pre-existingSV_*values, while its Story/Tutorial constants remain unchanged. Pinned bytest_boss_remote_config.gd. -
Expose Supernova’s post-230 balance levers through remote config. Evolution area, cadence, burst damage, remnant radius, and remnant DPS are code literals today, so tuning its new big/slow/remnant identity would otherwise need another App Store build. Keep remnant lifetime shared with Wildfire’s
ElementalSystem.ZONE_DURATIONunless a future pass deliberately splits it. ✅ DONE (c142f22f): five keys are inremote-config/manifest/build-230.json, with a live evolved-pulse test covering all of them. -
Remote-config batch 3: elites tunable + a mechanical liveness gate (2026-09-30). The elite promotion/stat family and the whole EliteKit attack roster are now patchable without a store build (67 new keys; 84 tunables total in
remote-config/manifest/build-230.json), with structural bounds: elite contact mult capped 1.47 (keeps an elite Swarmer’s hit under the Warden’s 44 swing), radius mults capped so promoted enemies stay insideMAX_ENEMY_RADIUS(80).ELITE_XP_MULTis DEAD (multiplies only the never-readxp_valcolumn) — stays const, removal logged in NEXT_TASKS.md. ✅ DONE (1ce0dbd7): plustools/remote_config/liveness_probe.gd— the per-batch hand-trace is now a ~5-minute automated sweep (one deterministic run per key, digest-diffed vs baseline; bible fields included, so the “manifest offers stats nothing reads” class is caught before a patch ships), and theBIOMASS_TRIGGER <= TARGETcross-key rule is coded intoTunables.apply+publish-config.mjs(no more eye-check). ATV frame-time check for the BUILD 230 keys (Supernova/boss + elite batches; batch 1’s passed 2026-09-28 on BUILD 205) still pending with Chris — don’t publish any pacing/elite patch before it runs. -
Beacon flank-reduction buff never wired.
BEACON_FLANK_MULTis referenced nowhere though its header says_move_enemiesreads it. Wire it or delete it (Chris’s call on intent). -
Drone shop copy + dead Disruptor: Power (Chris, 2026-09-29: “the decoy seems to have some upgrades which don’t make sense” + “put an explanation against each drone type”). ✅ DONE (
e0bc848e, 2026-09-29). Three fixes: (1) the legacydecoy-power/decoy-durationpair scales Decoy/Bomber/Interceptor damage and EVERY class’s lifespan but read as duplicates of “Decoy: Power”/“Decoy: Endurance” in the Decoy tab — renamed “Drone Power”/“Drone Lifespan” (per the 2026-09-27 plan, ids unchanged), routed to the General drone section,dronesicon; (2) “Disruptor: Power” was a dead purchase (the class dealt no damage at all) — the slow field now chipsDISRUPTOR_DMGper 0.5s pulse, scaled bycfg.dmg, and the misleading “+25%damage / repair” descs were corrected per class (only Logistics repairs); (3) every Drones shop section now carries a one-line what-it-does/what-it’s-good-for blurb ( ShopCategories.DRONE_SECTION_INFO). Suite 343 scripts / 3445 tests green. -
Decoy TYPE cards removed — the decoy is one basic low-damage taunter (Chris, 2026-09-30: “the different types of decoy don’t really make sense, decoy should be a low dmg taunter and thats it”). ✅ DONE (2026-09-30). The four unlock-decoy-* cards (Striker/Bulwark/Mender/Swarm) left bible.json (66 → 62 shop items), and with them the whole type machinery:
DECOY_TYPES,Sim.decoy_type,MetaState.selected_decoy/owns_decoy(), the shop’s equip-on-retap path, theDecoyState.extracompanion system, thedecoyshop icon, the runs-row decoy telemetry field, and the economy sim’s priciest-decoy equip heuristic. What flies is the basic cfg (sentinel_cfg()), levelled only through the drone-sentinel-* stat cards. Save-safe: old saves with a selected type / bought cards load fine (orphaned levels sit inert; the gold still refunds via the full-shop Reset — the gold_spent ledger never keyed on ids). Suite 343/3441 green, determinism baselines held; catalogue-cost pin 22951 → 21701. -
Fresh-install ~1 s shader stalls on every Apple device: the bake never contained
.metallib(2026-10-06). Xcode 27 ships the Metal Toolchain as a separate download and this Mac lacked it, so every export loggedMetal shader baking limited to SPIR-Vand shipped MSL source; the device compiled each shader on first use (~1 s on the ATV), hidden by the per-app OS shader cache until a fresh install. Every first launch in telemetry stalls (store 255 iPad 4.1 s, iPhone 1.5 s; ATV 260 reinstall ten ~1 s stalls). Toolchain installed; exports now fail on that warning (91cc7c6b). BUILD 262 Dev = first metallib-baked ATV build, installed onto a deleted app. Remaining: (1) verify 262’s fresh launch has no ~1 spipelines > 0rows; (2) the next store build is the real fix for players; (3) hitch tracking only runs mid-run, so menu-time stalls are invisible. -
~1 s first-use shader stalls on Apple TV/iPhone: our custom shaders are never baked. The shader baker (
editor/export/shader_baker_export_plugin.cpp_customize_resource) only bakesMaterialresources found in exported files/scenes. All 21 of ourShaderMaterials are built in code (ShaderMaterial.new(), 15 of them from inlineShader.new()code strings, e.g.render/screen_punch.gd,render/shockwave_layer.gd,render/gravity_lens.gd), and there are zero savedShaderMaterial.tresfiles, so the baker only covers Godot’s built-in canvas shaders. Telemetry: BUILD 208 (baker actually running) still had 0.96-1.3 s stalls with 1 new pipeline each (a boss-down moment and one mid-play); iOS store BUILD 206 had 3 up to 3 s. Fix: move each shader to a.gdshader+ savedShaderMaterial.tres,preloadit (duplicate per instance where uniforms differ), and check the baker’s step count rises above 202. Fix mergedd82c6260(baker 202 → 328 steps, guardtests/test_baked_shader_materials.gd), on the ATV as Dev BUILD 209 — tick only after its telemetry shows the stalls gone. D1 hitch check 2026-09-30 — fix works, but NOT tickable yet: stalls remain. BUILD 211 on the ATV (post-1080p, ~2.4 h of samples) is CLEAN: 103 hitches, none withpipelines > 0, max 120 ms. But 209 still had ONE 1.26 s stall in a modal at run_time 602 s (3 new pipelines; its other 484 hitches are 4K-render-era noise, pre-1080p), and BUILD 212 REGRESSED in 17 min of play: 1.13 s at run start (recent = new:pipelines, 4 pipelines compiled, run_time 0) + 1.06 s at run_time 664 s. 212’s only functional delta is84cc802c, which edits two ALREADY-SAVED shaders (star_field.gdshader,nebula_bg.gdshader, both carry.tres) — suspect: EDITING an existing shader dodges the fork’s bake cache (unconfirmed); the other suspects are the unbaked engine-material classes below. BUILD 213/214 have zero telemetry samples (never run on any device), so the verdict needs the next ATV dev session: watchpipelineson any hitch ≥500 ms. BUILD 215’s first session (2026-09-30, ~4 min): 3 hitches, ZERO withpipelines > 0, max 85 ms — no 212-class run-start stall. 215 carries 212’s same two edited shaders baked cleanly, which supports the stale-bake-cache theory for 212, but the item stays open until a longer session (boss-down moment, an area change) confirms it. Follow-ups (reviewer): ~45CanvasItemMaterial.new()+ 3ParticleProcessMaterial.new()(fx/gpu_burst.gd,fx/thruster_trail.gd,render/funzo_confetti_mist.gd) are still unbaked (engine registers their shaders non-embedded); the guard test only scansrender/ ui/ fx/ main.gd. Also still open: BUILD 208’s game-over hitch was 503 ms of script time (proc_ms). 2026-09-30 root cause + fix (BUILD 217): the reviewer’s “unbaked” split was half wrong. The 45CanvasItemMaterial.new()sites were NEVER missing from the bake — they all share one baked defaultCanvasShaderRDversion (27 variants in the export cache), which is exactly why BUILD 211/215 logged zeropipelines > 0hitches despite them. The real gap was the 3ParticleProcessMaterial.new()sites: each distinct code-built config composes its own particle shader version at RUNTIME (damping / curve / ramp feature toggles), none in the bake cache → full source compile on first use. That was BUILD 216’s 494 ms 5-pipeline run-start stall (new:state:play,new:pipelines, fresh process) and BUILD 212’s 1.13 s equivalent. Fix: the 3 process materials saved as exact-config.tres(render/shaders/{thruster_trail,gpu_burst, funzo_confetti}_process_mat.tres) + preloaded at their sites — verified empirically that the fork’s baker DOES bake saved ParticleProcessMaterial .tres (bake 202 → 204 steps, 2 newParticlesShaderRDcache entries);tests/test_baked_shader_materials.gdnow bansParticleProcessMaterial.new()in shipped dirs. CanvasItemMaterial sites stay as-is (shared baked version; residual per-PSO creation cost only, small). Untick this only once BUILD 217+ ATV telemetry shows a fresh-process first run with NO run-startpipelines > 0hitch. Same day, part 2 — the un-bakeable half is PSO linking. BUILD 217’s fresh-process first run STILL hitched at run start (762 ms, 4 pipelines,proc_ms9 = pure render thread) — but with ZERO runtime shader-cache writes on the ATV and the driver’scom.apple.metallibrary cache not touched during the run, i.e. no source compiles remained anywhere. What’s left is Metal pipeline-state-object LINKING: per-process, on each pipeline’s first submit, not serializable, not bakeable. Fix part 2:render/prewarm_pass.gd, wired inmain._show_start_menu— draws one invisible instance of every run-start pipeline family (multimesh ADD quads, both particle compute+draw passes, additive textured quads, ADD primitives, ArenaBackground’s star multimesh, NebulaBg’s offscreen pass) for 24 frames behind the start menu, so the menu period pays the PSO links where a stalled frame is invisible. PSOs then exist before any first run. Measured, prewarm v1 (BUILD 218, fresh-process attract run): run-start hitch 762 → 126 ms (4 small PSOs left, ~30 ms each) + 105 ms at first blaster_fire (3 PSOs) — the heavy links are gone, but the ≥60 ms bar is not met yet. Prewarm v2 (BUILD 219) adds the four families the menu never draws — HeroLights’ PointLight2Ds (zero 2D lights exist at the menu), ArenaBorder’s shader strips, Polygon2D (ship core / turret hexes), outlined dmgnum labels. ✅ VERIFIED ON THE ATV (2026-09-30, BUILD 219 fresh-process attract run): ZERO hitches — not even a sub-60 ms one, 200 enemies on screen 74 s in. Run-start stall history: 494 ms (216) → 762 ms (217, post-bake, PSO-only) → 126 ms (218, prewarm v1) → 0 (219, prewarm v2). The ATV draws nothing cold at run start any more. ⚠️ One narrow residue, iPad only: the same BUILD 219 iPad attract session still hitched 129 ms + 107 ms with the 4+3-pipeline shape. Since the ATV is fully clean, those 7 must be iOS-only first-draw families the ATV never renders — prime suspectTouchControls(main._new_run adds it on iOS only; its joystick/buttons first-draw at play entry; the menu never shows it). Follow-up: prewarm TouchControls (and any other iOS-only run-start node) the same way, then verify a fresh iPad run. Status 2026-09-30, after verifying BUILDs 220-222 on the iPad — three findings: (1) TouchControls prewarm landed (family 11,5faaa362) and is verified NOT the fix. BUILD 220’s fresh iPad run still hitched 131 ms / 4 PSOs at run start + 98 ms / 3 at first blaster — the same shape as 219, so the pad’s families were never the missing ones. The prewarm stays (correct coverage, zero cost, tvOS no-op). ATV on the same BUILD 220: CLEAN (two benign sub-80 ms rows, zeropipelines > 0) — the ATV half of this item is closed. (2) Root cause: the stock Godot 4.7.1 iOS export baker is thin. The iOS-Dev export bakes ~16 distinct RD cache entries (~1 CanvasShaderRD variant) vs the fork’s ~137 (19 CanvasShaderRD variants). The tvOS app (fork runtime + fork bake) links those variants invisibly during the menu prewarm; the iPad (stock runtime + stock bake) pays a ~100 ms source compile+link per missing variant at its first submit — exactly the 131/98 ms rows. The STORE iOS path has the same weakness (build-ios.shexports with stock godot — store BUILD 206’s 3 s stalls). (3) A fork-baked pack inside the stock runtime REGRESSES (tried + reverted same day). BUILD 221 (fork--export-pack): 1243 ms run start, 705 ms first level-up, a cascade of 300-700 ms rows (8 total) — the stock runtime can’t read the fork’s bake cache and compiles more from source than stock’s own 16 entries covered.install-dev.shreverted to stock with a warning comment; BUILD 222 redeployed to restore the 220 state. (222’s first observed session came back CLEANER than 220’s baseline — no run-start or first-blaster row, one 305 ms first-new:novastall at 24.7 s; plausibly the 221 session’s runtime compiles persisting in the app’s on-disk cache across the reinstall, so don’t read that as the bake gap being gone — a true first install should still show the 131/98 shape.) The real fix is the fork runtime + templates inside the iOS app (the tvOS model) so fork-baked packs are first-class — a release-pipeline project that also changes the store build path. Chris’s call on scope/timing vs 1.0.1. Tick THIS item only once BOTH devices’ fresh first runs are clean; the iPad half now waits on that decision (standing residue: ~131/98 ms). -
Debug Settings panel traps you on tvOS (Chris, 2026-09-30, mid audio A/B: “I wasn’t able to get out of the debug menu, the game carried on in the bg”). ✅ DONE (same day). The panel’s
_inputhandled only up/down/A, so the Siri remote’s Back did NOTHING — the only exit was swiping down ~25 rows to the bottom “Close” button while the sim kept running behind the panel (opening it from the pause menu hides the menu and pauses nothing;main.gdonly restores the pause menu whenclosedfires). Back (JOY_BUTTON_B), Esc andui_cancelnow emitclosed, exactly like the Close button — the pause menu returns via the existing await. Dev-builds-only surface. Pinned intests/test_debug_settings_panel.gd(back closes + hides; Esc closes; back ignored while hidden) — 60/60 panel tests green. -
✅ DONE (2026-10-01, 2bbad3ee) Firestorm zone + Thunder Wake puddle now drawn. Both persistent DoTs lived on the weapon instances and had no renderer;
ZoneRenderer.weapon_zone_discsnow draws them (FX v2 batch 2). The Railgun mark lands with the batch-2 Railgun shootout winner. -
✅ DONE (2026-10-01, 4ebccfcc) Railgun mechanics batch (FX v2 batch 2, Chris-approved A1/A2=B1/A3/C). The lance fx event carries
target_dist; a marked enemy that gets hit flashes (hit-confirm,EnemyPool.last_hit_time); a charge tell latches the aim 0.1 s before the shot and the shot follows it; an expiring mark or a marked death detonates for 0.25 x lance damage within 40 px (queued, resolved at the next Railgun update, never mid-sweep). All three knobs are remote-config tunables (WeaponRailgun.TELL_S/MARK_POP_FRAC/MARK_POP_RADIUS),0= the old behaviour, so Chris can A/B them; all render consumers are FxManager-owned, so they show on v1 and under every candidate look. Branchfx-v2-batch2-beam-railgun; still needs Chris’s on-device feel check. -
✅ DONE (2026-10-01, 7da4f5e6) Pulse, Beam and Railgun all read the same yellow (Chris, Apple TV). bible light (#fff3a0, S 0.37: Beam + Railgun) and lightning (#ffe34d, S 0.70: Pulse) both hit
ElementPalette.vivid()’s S >= 0.92 floor and landed ~2° apart, (1, 0.88, 0.08) vs (1, 0.84, 0.00); all layers are blend_add, so nothing else separated them.ElementPalette.weapon_color()keeps light a pale gold for player weapon FX only: own hue, SWEAPON_LIGHT_SAT0.48, V floored = (1.00, 0.94, 0.52); lightning and every other element unchanged. FxManager uses it for every fx_event with awid(enemy beams keep the old colour) plus the Railgun mark, tell and mark pop. A/B: Debug Settings “Weapon Tints (A/B)” (default ON, also the console’s graphics toggle); remote:ElementPalette.WEAPON_TINTSkill switch +WEAPON_LIGHT_SAT(0.3-1.0) so the gold can be tuned on device without a build. Branchfx-v2-promote-railgun. Needs Chris’s eye on the TV: whether 0.48 reads gold or cream once additive overlap pushes it bright (raise the tunable toward 0.55-0.6 if cream). -
✅ DONE (2026-10-01, 445ce1ec) Evolved Beam swamps the screen (“all of them become a bit too much once evolved”). New Beam board letter E = letter D toned down (archive rule: D untouched): halo quad 1.6 half-widths max and capped at 14 px, held beam soft-capped at ~0.9 after BASE_DIM (no bloom), a continuing shot in a stream drawn at 50% with no flash, Lance rod/braid 0.6x. Compare D vs E in Debug Settings > Beam Look. Note the beam’s footprint is still the real hit width (the sheath IS the hit edge), so an evolved wide Lance stays wide by design.
-
Kinetic and aether render as pure blue (logged, not fixed). bible kinetic #c9d1d9 (S 0.07) and aether #e0e0ff (S 0.12) are near-grey, but
vivid()’s saturation floor turns them into (0.07, 0.49, 0.90) and (0.08, 0.08, 1.00). Hits Blade, Turret, Momentum Core (kinetic), Blaster (aether) and every kinetic/aether enemy. Same root cause as the light/lightning collapse; needs a design call on what kinetic/aether should look like before touchingvivid(). Other weapon pairs that also land close undervivid(): cold Orbit (0.02, 0.63, 1.00) vs time Chronowell (0.08, 0.70, 1.00), ~3° apart (Chronowell’s own FX use a hard-coded pale cyan, so it shows mostly in shared paths); fire (Wildfire, Nova) vs blood (Scar Tissue, Scatter), 10° apart. -
Seven player-facing mechanics have no renderer at all (audited 2026-09-30 with the new roster board,
tools/vfx_library/library.tscn). Ranked by gameplay impact: enemy elemental aura + stack count (core to the elemental engine — damage changes, nothing draws), the Firestorm zone and the Thunder Wake puddle (both persistent DoTs that already have sim state, so aZoneRendererconsumer is the cheap win), the Railgun mark (damage amplification with no visible tell), the Disruptor field radius, the Scar Tissue stack meter, and the Glass Wisp phase state (untargetable looks identical to solid). Evidence: the red MISSING cards on that board, with each item’s sim source path intools/vfx_library/library_manifest.gd. Fix these alongside FX v2 batch 2 (Beam + Railgun). -
Late-run fps collapse on the Apple TV (BUILD 233 telemetry, 2026-10-01). ✅ DONE (
a6361428, dev-installed on the ATV as BUILD 234). A 14-min attract run held 50 fps (the TV runs at 50 Hz) until webs, zones and swarms piled up, then sat at 16-27 fps for ~90 s. Pooled ATV samples (builds 228-233, ~6,100) pinned three costs: (1)ElementalSystem.drop_websscanned every web (up toWEB_CAP400) for every spider every tick -> now a per-call coarse grid, ~10x cheaper (3.7 -> 0.38 ms/tick on the Mac at 40 spiders / 400 webs), output proven identical to the full scan incl. cap eviction (tests/test_web_drop_grid.gd); (2)Sim.zones(Wildfire scorch mod + Supernova remnant, new at 64-cap from BUILD 232) drew 3 immediate-mode calls each -> one MultiMesh +render/zone_field.gdshader(48 zones: 254 -> 15 draws; web path pixel-identical, Metal within AA-edge noise after an HDR sRGB->linear fix that instance colours need), prewarmed as PrewarmPass family 13; (3) Wildfire rebuilt the spatial hash per detonation -> once per fuse batch. Determinism baselines held, late-game invuln soak clean. Verify on the ATV: a long run with spiders + Wildfire should hold fps windows near 50 where BUILD 233 dropped to 16-27. Not addressed: evolved Nova costs ~5 fps even with few enemies (cause unknown; ATV reports nogpu_ms). ATV verdict (2026-10-01, TV now 60 Hz): a 15-min BUILD 234 run averaged 57.2/60, worst window 41 (233 on the same 60 Hz: 46.2 avg, worst 16). Fix confirmed. -
Game-over screen stutters on the ATV (BUILD 235 telemetry, 2026-10-01). ~145 frames of 78-93 ms over ~60 s on the results screen in one run (fps 40-46);
pipelines = 0, proc ~15 ms, ~270 draws, so GPU-side, not a compile. Also logged on 230/231/233. Suspect the frosted GlassPanel card (ui/results_panel.gd; already uses a one-shot snapshot) or the focused button’s glow quad. Reproduces on every death, so profile it there first. -
Once-per-run ~1-1.3 s first-use pipeline stall on the ATV (BUILDs 232-234). One pipeline, low proc_ms, at 0:46 / 1:54 / 2:04 in three runs; not reproduced in 3 runs on 235. Until 234 its
recentcontext was always empty: the look-back was measured from the stall’s END (fixedee09dc90, which also tagsnew:node:/new:shader:first sightings). Next occurrence on 235+ names the cause — queryhitches WHERE pipelines > 0. -
Evolved weapons cost ~3-5 fps on the ATV with few enemies (BUILD 234: flat 60 before evolutions, 55-58 after, sim_ms flat). Suspect the Supernova remnant zone’s fill (zones=1 for the rest of the run). Measure with
perf_probe --zones=1at remnant radius. BUILD 235 runb72fe12d(autopilot, 2026-10-01) — worse than 3-5 fps, and it’s per cast: 60 fps with 150-200 enemies before Nova evolved at 3:46; after it, windows of 30-42 fps (worst with 100-175 enemies + 165-391 webs + a zone) andmax_frame_ms42-64 in almost every window, even clean-60 ones with <10 enemies (before: 24-38). All 3 hitches (61-64 ms, pipelines 0) havenovainrecent.render_cpu_ms0.5-2 vsrender_ms28-40 = GPU. Pooled 60 Hz ATV samples (233+): zones 0 → 57 fps, 1 → 54, 2 → 46, 3-10 → 38-49 with <30 enemies. Lead suspect: the Supernova shockwave fill per cast, then the crater. Nova* dealt 2.3% of run damage, Orbit* 0.05% — the costliest look is near-useless (balance call for Chris). A/B in flight: dev-channel patchb235-dev-001(REMNANT_FRAC0.1,remote-config/build-235.json); revert with a{}patch tobuild-235@devafter the run (it otherwise serves dev 236+). Clean on-device A/B tool: next item. -
On-device perf scenario in the Remote Playtest Console (Chris, 2026-10-01). One button: clear + invuln + spawns paused, grant/evolve a fixed loadout via the real crystal path, a ring of stationary targets, optional held webs/zones, then ABBA-alternate a RENDER-ONLY hide toggle (Supernova shockwave / zone layer / web layer — flippable mid-run, determinism-safe, like
perf_probe --hide-v2) in ~10 s segments, running the schedule on the device. Each segment pushes oneEventQueueevent (p50/p95/max frame ms, frames >33 ms, fx before the worst frame); the panel shows an A-vs-B table. Needed because remote-config tunables apply only at run start and the hitch log ignores the <60 ms stalls. Ships with BUILD 236 (+ RC-4’sLIFE_EVOLVED).- code done (
bbb0e922+ panel86f60420); control Worker deployed 2026-10-01 (version 45bfd495); pending: ATV acceptance run
- code done (
-
Web layer overdraw on the Apple TV (Chris, 2026-10-01: “slows down a lot when webs are on screen”). Webs are already ONE batched draw (
render/zone_renderer.gdweb layer, since BUILD 191), but each web is its own additive quad, so up toWEB_CAP400 overlapping webs (radius 38, spacing 34; Elite Spider capsules radius 100) shade the same pixels many times on a fill-bound GPU. Weapon zones got a union shader for exactly this (one fill per pixel,zone_renderer.gdweapon-zone section, WZ_MAX 64); webs did not. Measure first with the perf scenario (webs=400, armwebs), then consider a union/low-res approach — 400 webs exceed the zone shader’s 64-slot union. -
✅ DONE (branch crystal-card-names; numbers were consistent, “6 more” = still needed AFTER the grant, so the body now starts “Then N more X:”; every player-facing element label reads the bible name via
ContentDB.element_name) Level-up crystal card: title and body disagree, and the title uses the old element name (found 2026-10-06 in the Mac App Store screenshot run). The card read “+3 Lightning, +4 Void” while its body said “6 more Void and Beam reaches 25% further” (+4 vs 6, and the body never mentions the Lightning/Electricity crystals) (seen in a 2880x1800 capture viashoot.gd, level 26). Two problems: the title says “Lightning” wheredata/bible.jsoncalls the element Electricity, and the title/body counts may come from different sources. Find where the card builds its title vs body (ui/upgrade_copy.gd/ the level-up panel), make both read one value, and use the bible’s element display name. The Mac listing shipped without this shot until it is fixed. -
The first warp in a tinted Dimension wipes the backdrop tint (found 2026-10-09 by the HDR step-2 final review).
ArenaBackground.set_tint()stores the Dimension tint as the backdrop’s ownmodulate(render/arena_background.gd:270), andmain._drive_warpwritesarena_bg.modulate = Color(dim, dim, dim)every warp frame and leaves it WHITE at warp end. So after the player’s first warp in Void / Poison / Fire, the arena backdrop loses its tint until the next area change (main.gdarea switch) or a live HOOK_TINT. Fix: keep tint and warp dim separate and multiply them (e.g. ArenaBackground holds_tintand_warp_dim). This interacts withcolour_space_fix(branchhdr-colour-space): there the star-layer compensation must also take the tint into account, so fix it on or after that branch. -
Un-remapped alpha layers on the linear canvas (audit 2026-10-12,
docs/research/2026-10-12-unremapped-alpha-audit.md). Glass glow 5.8-6.4x, enemy Nova glows 2.6-4x, far stars 5-9x brighter than 8-bit on Metal L0 SDR / HDR. Player hull glow ✅ DONE (e8e482e9) and UI P3 in HDR ✅ DONE (7772c7b8), both approved by Chris on BUILD 312. 📍 Execute plandocs/superpowers/plans/2026-10-12-unremapped-alpha-fix-batch.md. -
✅ DONE (fix/hp-clamp:
_hurt_playerfloors hp at 0,tests/test_hp_floor.gd; determinism baselines unchanged; the seed-1085003922 repro now scores 104.0 s with no invariant error) Pilot HP can go below 0 on a lethal hit (found 2026-10-13 by the persona benchmark,persona-offline-bench). Repro: the damage:strong elite of the epoch-2 prod archive (genome986ed32f08954dce), seed1085003922, skill strong, learner conditions (EvolveBatch.soak_persona): SoakRun invarianttick 6241: pilot hp out of bounds: -0.011623/100. Pre-existing, not from that branch. Likely cause (reviewer, [Likely]):Sim._hurt_playerdoespilot.hp -= takenwith no clamp (sim.gd ~3324); the only clamp to 0 is inside_check_pilot_hit(sim.gd ~3412, underif pilot.hp > 0.0 and not is_invulnerable(pilot)), so a lethal hit from any of the ~20 other_hurt_playercallers (burnelite_kit.gd:355, leechenemy_behaviors.gd:705,graviton.gd:178,funzo.gd:317, hazards, boss attacks) leaves hp negative. SoakRun’s tolerance is -0.01 (soak_run.gd:279). Side effect: the soak learner and benchmark silently drop every run that dies this way (no sample), a small survivorship bias against genomes that die to DoTs. Fix: clamp in_hurt_player. May touch determinism: check whether hp feedsstate_checksum(); if a pinned baseline moves, re-pin via skillbh-determinism-repin.
2 · Tooling / process
Section titled “2 · Tooling / process”-
✅ DONE (2026-09-30)
scripts/gate.sh:32silently discards intentionalproject.godotedits (git checkout -- project.godot, meant to undo Godot’s own rewrites). Bit us 2026-09-28: a real rename was reverted before commit. Same line inscripts/economy-careers.sh:23. Fixed with the snapshot-and-restore option: both scripts snapshotproject.godotat start and restore it at the end (only if the run rewrote it, with a loud warning + the snapshot path) — gate/career-induced Godot rewrites are still reverted, but a developer’s intentional uncommitted edit now survives. Validated by a full gate run (PASS) and a real mini career run. -
marketing/template/make_press.pySHOTSpoints at renamed screenshots (02_drones.pngetc.; current set is01_horde … 06_stats). Chris picks the shots, then regeneratemarketing/appstore/press/one-sheet.png(footer URL already fixed in66a2f2b). -
~/Claude/bullet-heaven-sitehas no git remote — its commits exist only on this Mac. -
.claude/skills/is globally gitignored, so Toby doesn’t get the 19 project skills (bh-deploy,bh-appstore-release…). Decide how to share them. -
Audio perf pass (
docs/research/2026-09-29-audio-performance-audit.md, all 8 items): PCM SFX imports, idle-first voices + boss reserve + UI pool, per-play churn, music dedupe/preload/loop imports,SFX_COOLDOWN_TICKS, HardLimiter,DevFlags.audio_sfx_off,aud_*sample telemetry. Merge ✅ DONE —fe29d3d8(2026-09-29) already carried the whole branchperf-audio/2026-09-29; this line was stale. Live D1 + Worker ✅ DONE (2026-09-30): the 7aud_*ALTERs applied one command each (telemetry/migrations/2026-09-29-audio-telemetry.sql),deploy.sh --check-onlygreen, Worker3231abe6deployed, smoke test passed (aud values land; old-style no-aud samples still insert with NULLs; dedup intact; smoketest rows deleted). Dev builds ≥ BUILD 211 already sendaud_*and it is now stored, so the ATV A/B needs no new build: flipaudio_sfx_offin Debug Settings (live flag) and compareaud_us/sim_msper window. Still open: Chris by ear on ATV/iPhone (cooldown table, limiter at loud pile-ups, boss stings in busy fights); web first-boss-spawn trace (needs the web demo rebuilt + deployed from main first). Dashboard note:aud_*is ingest-only — the dash tables don’t render audio columns yet; query D1 directly for the A/B. ATV session verdict 2026-09-30 (Dev BUILD 215, 237 s autopilot survival run, 586 kills, lvl 16): audio data side ✅ DONE.aud_usp50 2.9 ms / p95 5.2 ms per 5 s window = 10-17 µs per tick at 35-152 enemies ≈ 0.3% of the ~3.7 ms/framesim_ms— PCM did its job; audio is no longer worth chasing on ATV.aud_mix_rate48000 on-device (closes the audit’s [Likely] HDMI-48 kHz question);audio_sfx_offverified live (aud_us → ~14 µs, timer residue). The pool hit 16/16 in the dense fight with 21 steals over 57 windows (the #3 boss-sting steal risk is real — the ear test decides if it’s audible);aud_capped275 of ~2 900 plays (~10% dropped by cap/cooldowns — the cooldown table is biting). The planned on/offsim_mscomparison is moot: the off windows were the run’s death tail (0-1 enemies — Chris got trapped in the debug panel, bug below), andaud_usmeasures the audio share directly anyway. -
The docs site has been silently FROZEN since 2026-07-14 — found in the 2026-09-30 save housekeeping. Every push since the first frontmatter-less
docs/superpowers/{specs,plans}file landed (07-14) failed its CF Pages build (Starlight requires a fencedtitle:on every content file; 28 specs/plans had none, and the 09-26 mobile-touch survey had an unfencedtitle:line); CF kept serving the pre-07-14 snapshot while everyone believed pushes deployed. ✅ DONE (6a4edf67, 2026-09-30): frontmatter on all 29 files + gate-run guardscripts/test/docsite-frontmatter.test.mjs(lists EVERY offender — the CF build only reports the first). Live site verified current again (survey, audio audit, specs pages all 200). Lesson recorded: a 200 from the site proves nothing — checkwrangler pages deployment list --project-name=dark-cosmos-docs. -
Remote Console v2 (Chris, 2026-10-01: “a toggle to turn upgrades off, reset everything, remove crystals, bigger more graphical panel, better organised, graphical tooltips”). No XP dev toggle (console + Debug Settings), Reset everything (build + enemies + gems), crystal removal that rolls back rungs (mod log + weapon rebuild), and a full-width neon mission-control panel with hover/long-press tooltips. Spec
docs/superpowers/specs/2026-10-01-remote-console-v2-design.md, plandocs/superpowers/plans/2026-10-01-remote-console-v2.md. ✅ DONE (branchconsole-v2; merged dd208755) -
Remote Console: Looks as one-tap buttons + a What’s new tab (Chris, 2026-10-02). Every Looks row (Background, Ship tilt, weapon looks, Web look) is a wrapping button group that sends on tap and lights the live option (game status now reports
bg+tilt). New What’s new tab lists player-visible changes per BUILD with where to test them (control/src/whats_new.js); whoever merges a player-visible change adds abuild: nullitem, andscripts/bump-build.shstamps it on main/release/*. ✅ DONE (branchconsole-looks-whatsnew). Needs a Worker deploy to go live.
3 · Triage carry-over
Section titled “3 · Triage carry-over”-
Walk the still-open items in
docs/RELEASE-0.1.md(§1 gates likeADAPTIVE_QUALITY, §4 fix-if-cheap, §5 parked) and move what belongs in 1.0.1 here; leave the rest parked. -
Code cleanup audit (PARTLY DONE: zero-risk deletions + doc drift = PR #19
2c0b83c, teaser overlay843c3d5; the rest needs Chris’s calls or is structural):docs/research/2026-09-28-code-cleanup-audit.md(dead code, A/B look switches awaiting Chris’s pick, dev-only consts inmain.gd,sim.gd/main.gdextraction plan, stale worktrees with 3 unmerged commits, doc drift). Pick what goes in 1.0.1 vs later.- ✅ DONE 2026-09-29 (branch
cleanup/dead-code-2026-09-29:e0b662b,9b67605,1faf1e6,cba291f,1984acb,cff72dd+ the doc-drift commit): the zero-risk deletions — §1 dead sim symbols / consts / functions,primed/react_cd, the teaser-wormhole machinery,_archive/,verify-sim-refactor.sh, the 4 risky tests, §8 doc drift. Determinism unchanged, no re-pin. Still open: everything that needs Chris’s call (power-ups, Boss2, Warden elite, Beacon flank, A/B switches,ADAPTIVE_QUALITY, music dup), the Halo Chain/splitter biomass bug,xp_val+add()redesign,Elemental.apply()/ContentDB.reaction(), and the structural extractions.
- ✅ DONE 2026-09-29 (branch
-
Chris:
audio/menu_theme.mp3andaudio/survival_loop.mp3are byte-identical (3.7 MB each, both shipped). Intended, or was the survival loop meant to be different music? (Branchperf-audio/2026-09-29makessurvival_loopreusemenu_theme’s stream at runtime, so web decodes it once; the file itself is still shipped until this is decided.) -
AFTER HDR P3: GPU fill-rate pass + 4.8 sim-CPU profile (Chris, 2026-10-11). The ATV GPU-bound scene is fill-rate bound (
perf_fx_half_reson = 16.7 ms); 4.8 beat 4.7 by +21% frames (ABA). Start with an Xcode GPU frame capture on the iPhone (cable) for per-pass cost. Findings + ideas:docs/research/2026-10-11-godot-48-gpu-ab-and-fill-rate.md.
3b · Features in 1.0.1
Section titled “3b · Features in 1.0.1”-
HDR: widen the UI to Display P3 too (Chris 2026-10-12, “increase saturation of all UI elements in HDR mode”). Reverses the 2026-10-11 “UI stays sRGB” rule; HDR-only, SDR bit-exact. Branch
hdr-ui-p3(one pathColorGamut.ui_out= the LiveTokens output filter, own dialColorProfileHdr.GAMUT_UI_WIDENseed 1.0; arch docdocs/architecture/ui-tokens.md§ HDR Display P3). Needs review + merge, then Chris’s ATV/iPhone eyeball (dial 0 / 0.5 / 1). -
✅ DONE (hdr-colour-space: code through 6902d940, BUILD 310 6bbf8e68; arch doc
docs/architecture/rendering-and-performance.md§ Display P3 output) Display P3 wide-gamut output with HDR (plandocs/superpowers/plans/2026-10-11-hdr-p3-palette.md). Patched 4.8 forkViewport.output_gamut(iOS + tvOS),ColorGamutwidens gameplay colours to the P3 edge only while HDR output is engaged; UI stays sRGB. Baked BUILD 310 on iPhone 15 Pro Max + ATV, A17 baker check passed, ABBA fps shows no visible cost (iPhone 70.1 vs 70.6, ATV 59.78 vs 59.94 at the 60 cap, both inconclusive; the <= 0.1 msrender_cpu_msbudget was NOT measured, samples don’t tag the gamut state). Chris (ATV): “everything on looks best”, seeds stayGAMUT_P3true /GAMUT_WIDEN1.0. Still owed before main: merge review (plan Task 10) and the fresh-install stall check. Follow-up: glows a bit soft, especially on the player. -
✅ DONE (fb/health-bar-always-visible; pause-menu “HP bar Always/Auto” half-row beside Ship stats, Debug Settings row + console
touch_flag, saved; needs Chris’s ATV eyeball) Settings toggle: hull/health bar always visible (Chris voice feedback, D1, 2026-10-11, BUILD 297 ATV). Settings row + Debug Settings/console parity row; persisted in the save. -
✅ DONE (fb/player-death-sequence; render-only, skippable, master switch + every value a live tunable, vfx_library entry; flash 0.35; needs Chris’s ATV/iPhone eyeball, ring size and whether players get a flash/shake off-switch are his calls) Player ship death sequence: a big wide exciting explosion before the results screen (Chris voice feedback, D1, 2026-10-07, BUILD 267 ATV). No player-death FX exists today (
main.gdsim.game_overbranch goes straight to_show_results_when_drawn;fx/death_dissolve.gdis enemies only). Design call with Chris first; needs a vfx_library entry + score. -
Supernova “covers the screen” (TestFlight #27, 2026-09-27, iPhone11,2). The fps half is tracked under the evolved-weapons perf item; the screen-coverage half is not. 445ce1ec toned down evolved Beam, not Supernova. Chris’s call on size.
-
Supernova iridescent look + x4 damage / half rate + shockwave (Chris, 2026-10-12). Branch
fb/supernova-iridescent(stacked onfb/supernova-visual-size). Look: thin-film hue cycle + thin white-hot filaments (NovaBurstLook.EVO_IRIDESCENCE/EVO_HOT_THREADS, live; 0 = old look). Balance:EVOLVE_DMG_MULT7 -> 28,EVOLVE_COOLDOWN_MULT2.5 -> 5 (burst DPS x2), crater absolute dps kept (REMNANT_DPS_FRAC0.25 -> 0.0625). Shockwave: enemies thrown out (bosses + kinetic-immune never, elites x0.5), enemy bullets turned outward (WeaponNova.SHOCK_*). Needs Chris’s eye: SDR + HDR, several Supernovas overlapping. -
Re-judge “too hard, elite Glass Wisp has too much health” (Chris voice feedback, D1, 2026-10-07, BUILD 273 ATV) on a build with
50f4ef18/8463532c(the Orbiter/Wisp HP cut below). -
Elite Orbiter + Glass Wisp HP cut (Toby playtest via Chris, 2026-10-07: “some enemies, especially elite forms, have too much health, notably the Orbiter and the Glass Wisp”). ✅ DONE (branch
elite-hp-orbiter-wisp): new tunablesSim.ELITE_ORBITER_HP_MULT12 (was the generic 27x: 16 → 432 HP, now 192) andSim.ELITE_GLASS_WISP_HP_MULT6; Glass Wisp bible HP 12 → 8 (plain wisp 8, elite 324 → 48 nominal; the 50% phase-out per hit roughly doubles effective HP, so ~650 → ~100). All per-type elite HP overrides now come from one helper,Sim._elite_hp_mult(tid), on both_spawn_oneandspawn_story_enemy. Biomass/XP scales with the mult, so elite wisps/orbiters pay proportionally less XP. Testtests/test_elite_hp_orbiter_wisp.gd. Pending: Toby/Chris feel-check on device; tune live via the console if still off. -
Ship stats: weapon navigation + every crystal rung (Chris, 2026-10-07: “navigate into the full ship stats and select between the weapons … we need to see all thresholds”). ✅ DONE (branch
stats-sheet-nav): up/down (d-pad / sticks / arrows) and row taps pick a weapon in both sheets (shoulders kept; a tap OFF the glass still closes), the always-on Rift Bolt gets its own row, and each weapon carries a per-SLOT glyph (ui/weapon_glyph.gd: circle/triangle/square/…; crosshair = Aim). The Crystals panel is one line per element (name | rung bar | count) with one glyph per rung of every owned weapon and the rung number under it where it fits; filled = reached, GOOD = the previewed card crosses it; the selected weapon’s glyphs are lit, the rest dimmed, and its elements sort to the top. Rows select on release (a drag scrolls the column instead). Aim’s path reads its real AIM_MODS effects (+50% damage, +1 pierce, …). Elements an owned weapon needs but you hold none of show as 0 rows. Tests:test_weapon_glyph.gd,test_stats_sheet_rows/view, pause + level-up nav. Needs Chris’s eye on device (iPhone tap targets, TV legibility of the glyphs). -
Nebula visual polish (Chris, 2026-10-07, autopilot preview). Organic look + 12 clouds, 0.24–0.42 opacity and 0.6 organic scale applied LIVE to ATV BUILD 272 and verified in device status (no problems/pending keys). Local implementation and refreshed ui-sweep also pass the gate. Await Chris’s visual verdict, then an authorized persistent patch for supported values; investigate organic-selector persistence separately. No rebuild/install or persistent patch publication; local code/baselines remain uncommitted.
-
Event Horizon: 7 shards + space-time warp (Chris, 2026-10-07) — ✅ DONE (branch
event-horizon-warp). Evolve ADDS +3 shards (cap 7; pre-evolve cap 4 so “+3” is always true); evolved ring bends enemy bullets and nudges enemies along the spin (lens stronger near shards; bosses immune); red lens + eddy VFX (VisualLook.orbit_warpA/B), blood-red tint, deeper hum. All strengths remote tunables, 0 = off. -
Event Horizon: Apple TV perf + feel check. M5 only so far: GPU +0.025 ms per evolved ring, sim 0.167 -> 0.253 ms/update at 500 bullets + 140 enemies. Escape hatches:
WARP_*= 0,WARP_GAIN0,orbit_warpoff. Also Chris’s eye on the VFX (library rating held at 4). -
Autopilot upgrade picks: always take the best card (Chris, 2026-10-04: “the autopilot needs to always make the best decision on what upgrades to choose”). ✅ DONE (2026-10-04, this commit):
SkilledAutoPlayer.pick_upgrade_for(sim, pilot, ids)ranks the offered cards with the SAME pureupgrade_effectsscore the level-up panel badges BEST (rung-crossing crystal grant 20/evolve 100 > new weapon 15 > long-term crystal 9+8×closeness > plain stat 8 > dead 0), ties keep offer order, and a tutorial lesson’s forced card always wins its own step (pick_crystal completes only when its card is applied). Callers routed: main attract/capture, soak, autopilot_bench, profiles;ExperimentalUpgradePlayer.PRIORITYkeeps the OLD prefix heuristic as the A/B control arm, and story traces keep the context-free path. Paired-seed soak (24 seeds, skilled AI, only the picker differs): mean death 6961→7574 ticks (+8.8%), 17/24 identical, 5 better / 1 worse, best run 6958→21441 (lvl 11→22); death-cause mix and build diversity unchanged. Follow-up (economy sim): profiles inherit the smarter picks, soSkillProfilecalibration vs human targets needs a re-run ofscripts/economy-careers.sh(the old “strong is capped by SkilledAutoPlayer” note inai/skill_profile.gd). -
Weapon FX v2 — foundation + batch 1 (Chris, 2026-09-28) — ✅ DONE (2026-09-30: merge
10cfb863+ requirements commit): merged as reviewed per Chris’s T16 sign-off; PrewarmPass family 12 covers the six look pipelines + the legacy plasma quad (switch-gated);VisualLook.weapon_fx_v2default ON; every look material saved asfx/weapons/<look>_mat.tres(the branch’s code-built materials would have compiled ~1 s/shader on device — caught by the baker guard at the gate);bolt_impacthas its ownBOLT_IMPACT_CAP; windowed shader compile-checks clean under both renderers; ui-sweep baselines re-blessed (the 2026-09-24 ones predated Plasma Nova + the legacy Blade arc fix — drift verified deterministic + flag-independent before blessing; menu/shop captures unchanged). Specdocs/superpowers/specs/2026-09-28-weapon-fx-v2-design.md. Per-weapon shader looks that grow with level and transform on evolution; batch 1 = Blaster (all bullets), Orbit Shards, Blade, Scythe, Nova. Fixes Blade/Scythe 360° evolutions drawing a ~97° crescent. Later batches (Beam width bug, invisible Firestorm / Thunder Wake zones, Chalice element colour) ship in later updates. Plandocs/superpowers/plans/2026-09-29-weapon-fx-v2-foundation-batch1.md, implementation merged from branchweapon-fx-v2-batch1(2026-09-30). T16 sign-off (Chris, 2026-09-30): MERGE AS REVIEWED — orbit perf accepted, void accent kept; bolt_impact gets its OWN frame budget (BOLT_IMPACT_CAP 16; Chris’s call after the shared-vs-separate explanation). Merge requirements, same day: (1) the branch predates PrewarmPass — its run-start draw families (v2 PlayerBoltRenderer + thefx/weapons/looks) go intorender/prewarm_pass.gdalongside this merge, or they PSO-link on device at first bullet/slash; (2) windowed shader compile-checks under BOTH rendering methods (the headless gate never compiles shaders — bit this branch’s own Task 13); (3) grep all tests relying on theVisualLook.weapon_fx_v2default before flipping it, and re-bless affected ui-sweep baselines. 223 device feedback → 224 (dim): Chris on 223: the looks overload the iPad and “make the screen very white”. Data: iPad render_ms 14.7→24.4 with v2 on; ~57 hitch rows in one attract session, mostlypipelines: 0+proc_ms84–195 ms (main-thread spikes, incl. level-up frames), 3 rows actual PSO links (142/101/101 ms); ATV 50 fps but 3 first-use stalls (1747/652/602 ms) despite family 12 prewarm — consistent with the standing iPad bake-coverage gap (thin stock bake compiling the six NEW shader families from source on first run; per the 221→222 precedent the on-disk runtime cache should settle this over the next launch — VERIFY on the next session) plus real steady-state additive fill (render_ms). Fix shipped: BASE_DIM 0.75 — one knob onWeaponFxLayermultiplying every v2 look’s final rgb (alpha untouched;player_bolt+ legacy plasma already dimmed), warp dim scales by it. Commitdec86688, deployed as BUILD 224 both devices. VERIFIED on iPad same day (D1 attract runs, enemies 40-200): 223642hitches avg frame 25.2 ms → 224 session 1 exactly2(one-time PSO links: 371 msnew:blaster_fire×4 pipelines at t=2s, 241 msnew:novaat t=33s — both low proc_ms, the classic link signature, NOT the 223 proc storm) → 224 fresh session 2 zero hitches, 120 fps, avg frame 8.4 ms (better than the pre-v2 222 attract baseline of 14.3). The 223 storm is confirmed a first-session-after-install source-compile artifact (thin stock bake meeting six new shader families); it settled into the on-disk runtime cache, exactly the 221→222 precedent. Still open: Chris’s eyeball on the toned-down look (0.75 first guess, one constant), ATV first-use stalls re-check on 224 (needs a real run — its deploy only reached the menu), and a later-pass look at why family-12 prewarm didn’t absorb the two session-1 links (lit-run vs unlit-menu pipeline variant, or warm-window race — cheap to fix but session 2 shows the warmed cache makes it near-invisible; the sweep later VERIFIED family 12 warms under the same lit state as in-run, so the links were plain first-use). 225 (nova white-hot trim): Chris on 224: “the white hot nova is still a little too white hot when it has so many together” — the stacked additive white terms (ember white dots, core ×1.6, evolved shock ring ×2.0) trimmed ~25% (78cd1816). Eyeball verdict pending. ATV deep-run status (2026-09-30): the 223 ATV attract ran 287 s clean and DIED one wave short of the deep endgame; with the TV left on, 224+ attract runs reach 300-630 s (Accumulator elite + BOMB cascades + all six evolved v2 looks) and melt to fps 7-16, render 75-109 ms at 3-8 enemies — GPU fill-bound (the same frame is ~8.5 ms on the iPad; M5 fork-binary probe shows no engine pathology).ADAPTIVE_QUALITY := falsesince 2026-06-28 means no ladder catches it (Chris: enable the ladder only after no-tradeoff levers are exhausted, or make the ladder subtler). Shipped no-tradeoff batch (BUILD 226,39ab812d+c7265389, catalogperf-data/2026-09-30/analysis/no-tradeoff-sweep.md): Toby’s web capsules (elite landing 33 webs → 4; collision one segment test; circles byte-identical, baselines intact), batched look rebuilds (was up to 17 rewrites/frame on the evolved blaster build), frozen-frame enemy-sync skip (the 84-138 ms proc frames’ waste), change-gated ScreenFeedback pushes, dead StoryMapRenderer redraw. These remove the CPU half; the fill half needs a taste call (ranked levers: nova corona quad extent, bolt glow trail extent, orbit evolved arcs, dmgnum live cap — or the ladder). 226 regression → 227 fix: 226 melted the iPad too (fps 6-17, render 80-163 ms at t≈295, where 224/225 both ran 103-119 at the same window) — a real regression, not seed variance. Root cause:web_instancebuilt capsule quads asTransform2D(rot, size, rot, pos)and the 4-arg form’s THIRD arg is skew, so every capsule web was sheared by its own angle (a 45° web = a giant additive parallelogram; circle webs at angle 0 were unaffected, hiding it from the circle-pinned tests and the eye). Webs-present render: 16.4 ms (224, 299 webs) → 81.1 ms (226, 104 webs). Fixb3b43832(BUILD 227): explicit basis construction (_web_transform), tests now pin the RAW basis axes —get_rotation()/get_scale()decompose the basis and would still pass a sheared transform — and the hot loop is actually inlined (226’s commit claimed it). 227 verified on iPad: deep window 93-120 fps (where 226 hit 7-17), 400 webs at 13-16 ms render (vs 226’s 81 ms with a quarter as many). ATV 227: avg 44 fps / render 30.6 / sim 5 — CPU clean, fill wall remains (the taste call above). 228: evolved-nova trim round 2 (Chris, ATV Supernova “adjust that weapon some more”): ramp top off pure white, embers ×0.85 evolved, shock ring 1.6→1.3, corona 1.4→1.05 later linger,LIFE_EVOLVED0.70→0.60. Base nova untouched. + per-sample loadout telemetry:samples.weapons(“nova*,orbit”,*= evolved) on every 5 s row, so perf dips correlate with the build mid-run (runs only land at end; the ATV attract picks nova nearly every run, the iPad almost never — the fingerprint ends that ambiguity). D1 ALTER + Worker94ab833cdeployed before the build, per the schema deploy order. The fingerprint’s first catch (the real ATV/iPad fill wall): the SAME iPad run went 11.7 ms render → 91.5 ms (fps 112 → 30) the moment nova evolved, at 10-50 enemies, webs 0, draws low,render_cpu_ms0.4-1.0, sim normal — GPU back-pressure from the evolved Supernova alone. It was hidden until now: 226’s melt was web shear AND this (a nova* run), and 227’s clean verification run rolled a no-nova build. Evolved quad = 2 × 1.35 × (180 × 1.8 × radius mods) px ≈ full-screen on the iPad panel, while its content ends at ~1.14 — ~42% of the quad paid full fbm + 21-ember + 6-accent-motif cost for invisible pixels. 229 (e22fc541): evolved early-out at d>1.16 before all heavy work (softened by an evolved fade; base form untouched), ember loop gated to ember reach (lossless — the exp terms are e^-500+ out there), evolved ember budget 14→17 (was 21). Probe fixes: level-up deadlock unstick (frame_probe’s tick-N+1 fix) + dead-look rejection now only over ACTIVE weapons. Attract pauses Chris saw (2026-10-01): by design — each level-up freezes the sim and the AI picks after a 0.8 s beat (_auto_pick_timer), deep runs chain 2-3 picks (2-3 s pauses); 226’s frozen-frame sync skip made the world stop fully during them (previously it kept subtly animating). One-time PSO compile hitches ride the first evolved-nova draw (976 ms ATV, “new:nova”). 230 (111106b6) — Supernova rework (Chris: “increase its dmg, add dmg over time, halve or more its rate of fire”): evolved nova = big/slow/remnant — cooldown ×2.5 (was ×0.6, it previously fired FASTER), damage ×7 (was ×1.8; burst DPS ≈ par with the old ratio), and each evolved pulse drops a burning remnant zone at the pilot position (existing terrain-zone infra + ZoneRenderer element tint; radius 0.55×area, dps 25% of the burst per second, default 2.4 s). The slower cadence is ALSO the evolved fill-wall fix — on-screen Supernova overlap scales 1:1 with cadence.spawn_zonegained optional radius/duration/dps params (Wildfire unchanged, byte-identical); zones are NOT in state_checksum and no baseline evolves nova → determinism verified unchanged. New sim teststests/test_weapon_nova.gd. -
Telemetry v2 (Chris, 2026-09-28/29) — code ✅ DONE (Tasks 1-11 merged, last
db326aa); the Chris-gated steps G1-G13 below are open — specdocs/superpowers/specs/2026-09-29-telemetry-v2-design.md, plandocs/superpowers/plans/2026-09-29-telemetry-v2.md(task merges ingit log --grep "telemetry v2"). ⚠️ Nothing v2 goes to testers before the plan’s Chris-gated steps G1-G13: D1 migration →bash telemetry/deploy.sh --check-only→ deploy → smoke; App Store privacy label frommarketing/appstore/compliance.md+ site privacy notice (G5); on-device MetricKit (G6), render_cpu_ms (G10) and ATV Dev boot with the Sentry addon fetched (G11). Sentry DSN (G7, remove the.ioscall-stack override) and G13 only if Chris wants Sentry live. First Dev iPhone install needs a full iOS export (see dev-tools.md § Telemetry v2). Status 2026-09-29: ✅ G1-G3 (migration, Workerb6a45439, smoke), ✅ G5 (12-month retention cron31c7de5+ privacy page live), ✅ G10 + G11 (ATV Dev: clean boot, render_cpu_ms ~1.5 ms), ✅ G7/G13 decided: Sentry stays OFF for 1.0.1 (Chris; revisit only if MetricKit crash data proves insufficient). Open: G4 (Chris, ASC label), G8 (Chris runsgit add -f .claude/skills/; auto-mode blocked it), dashboard header eyeball. G6 check 2026-09-30 (not yet passable, blocked on the iPhone): zerometrickitrows in D1events(expected, not a failure). Verified from here: the sink IS in the current iOS Dev export (DerivedData-devbinary built 29 Sep 21:17 links MetricKit weak + carriesBHMetricKitSink), andOS.get_cache_dir() == NSCachesDirectoryon iOS is CONFIRMED — the iPad Dev container’sLibrary/Cachesholds ourcanary.txt(the plan’s [Likely], closed). But the iPad’s BUILD 210 install (launched 29 Sep 20:16) predates the 21:17 sink build, so that install can never produce rows; the sink-carrying install went to the iPhone (no launch event fromiOS/iPhone16,2since BUILD 206, i.e. it hasn’t been launched since install, and the device was unavailable to devicectl at check time). Remaining steps when the iPhone is next available: launch the Dev app, background, relaunch the next day, confirmmk-*.jsonappears then vanishes inLibrary/Cachesand ametrickitrow lands inevents(devicectl files cmd in plan G6). Low follow-ups from the Task 6 review (non-blocking):render_cpu_msskipsUPDATE_WHEN_VISIBLE/WHEN_PARENT_VISIBLESubViewports (threat radarui/threat_radar.gd:204, codex card) — count them when visible.net/telemetry.gd_sync_measuring.- One stale sample per run: a persistent SubViewport keeps its last measurement after measuring is switched off; a new mid-run SubViewport reads 0 once. Optionally skip the first read after enabling.
- Real GPU time on Apple devices needs Metal timestamp support in the
~/Claude/godot-tvosfork (MTLCounterSampleBuffer);gpu_msstays null on Metal/WebGL until then. Optional.
-
Warden rebuild (Nova look, FX, smarter Survival AI) (Chris, 2026-10-01) — ✅ DONE (75074990..45606e8a)
-
Warden: tutorial swing that misses draws no slash under warden_v2 ✅ DONE (6e8b1232) (final review M2, 2026-10-02).
WardenFxspawns the crescent onboss_swing_start(Survival-only) orboss_swing_hit(hit-only), so a dodged tutorial swing shows the ring then nothing. Render-only fix: latchswing_activerising edge inWardenFx.update_bosswhen no start event arrived. -
Warden: far-band lunge barely reaches (M5). ✅ DONE (6e8b1232): LUNGE_LENGTH 620 -> 760 (registry bounds stay derived 76..7600; no CROSS_RULES). Far
PREFERRED_RANGE700 vsLUNGE_LENGTH620 (reach ~700). Tuning call: LUNGE_LENGTH ~760 or far pref ~600 (both remote-tunable / static). -
Warden: device feel-check of the Survival AI (M7). Movement/aggression are headless-verified only and NOT behind
warden_v2(it gates looks only). Needs an ATV/iPhone playtest before a store build; also eyeball bolt size (Task 6 note), swing crescent thickness, enraged telegraphs now >= 0.45 s. -
Warden: spec deviations to acknowledge (M4): approach dash kept (gated beyond target range) instead of replaced by the lunge; reposition happens in APPROACH not REST; deck weights +
PREFERRED_RANGEare not remote-tunable (static Dictionaries); enraged pupil-narrowing not implemented. Classic look (warden_v2=false) misdraws SLAM/FAN/LUNGE telegraphs (M3) — ✅ M3 fixed (6e8b1232):BossRenderer.classic_overlay(). (range-aware attack deck, 3 new attacks, strafing + phase 2, per-attack damage telemetry, Nova body, projectile + telegraph looks behindVisualLook.warden_v2; specdocs/superpowers/specs/2026-10-01-warden-rebuild-design.md). Open: Chris eyeballs the captures, scores thewarden_*roster entries and feel-checks aggression (all weights/speeds are remote tunables). -
Chris’s BUILD 248 ATV feedback (2026-10-02) — Warden: “overall massive improvement”. New items:
- Orbit Shards VFX stays on screen after the weapon is removed via the Remote Console (bug).
✅ DONE (
f2e2e239, branchorbit-fx-fixes):dev_remove_weapononly erases the id; the arsenal keeps the WeaponOrbit + frozen positions, and both orbit renderers gated on the instance, never onactive_weapon_ids. Same pattern in other persistent weapons: see the next item. - Orbit Shards: one FX layer drifts over time / looks stretched when evolved ✅ DONE
(branch
orbit-fx-fixes): not the phase/extent – shader TIME (grows all session) fed fx_noise’s hash21, whose float32 precision collapsed (~4 hash values at 30 min): the lightning crackle stopped and the swirls went blocky/streaky. fx_noise now wraps inputs into NOISE_PERIOD (all v2 + Warden shaders). Repro:tools/weapon_vfx/orbit_drift.tscn. Needs Chris’s on-device confirm after a long session. - Evolved Orbit (Event Horizon) look rework: less of a solid ring, more distinct orbs with long
tails that read as solid (bh-vfx-iteration, toggleable A/B). ✅ DONE (branch
orbit-fx-fixes): comet orbs + flat-topped tapering tails, lens reduced to faint broken arcs;VisualLook.orbit_comets(default ON) + Debug Settings / Remote Console row “Orbit Comets (A/B)”. Awaiting Chris’s verdict. - Orbit Shards knock back enemies and enemy projectiles, scaling up with weapon level/upgrades
(gameplay change; touches determinism if baseline runs field orbit). ✅ DONE (branch
orbit-knockback): radial-outward shove through the sharedEnemyPool.knockbackimpulse (bosses immune, elites ×0.5, brute/wyrm/mid-dash/Halo immune as for the manual weapon) + speed-preserving outward deflect on enemy bullets; scales withWeaponVfx.investedand evolution. 6WeaponOrbit.*remote tunables. No baseline fields orbit — hashes unchanged. Needs an on-device feel-check of the numbers. - Every console-removed weapon leaves its deployed state behind (bug, follow-on to the Orbit
item): turret bodies, Thunder Wake puddles and Firestorm zones stayed drawn; Chronowell bubbles stayed
drawn AND kept slowing enemy bullets. ✅ DONE (branch
weapon-removal-cleanup):Sim.dev_remove_weapon(anddev_reset_upgrades) now callPilotArsenal.clear_deployed(per-weapon audit in its comment) and drop shared Wildfire fuses / Railgun mark pops once no pilot wields them; zone + turret renderers only draw equipped weapons. Dev path only, baselines unchanged. Tests:tests/test_dev_remove_weapon_cleanup.gd. Review round 1: also gives back an evolved Momentum Core’s Overdrive +10%pilot.speed(only reverted inside itsupdate()); no other weapon holds a pilot stat between ticks. - Co-op (gated off): two Chronowell pilots decay every bubble twice as fast – each pilot’s
WeaponChronowell.updateticks ALL ofsim.chronowell_zones, not just its own (found in the weapon-removal-cleanup review). Fix before co-op ships: tick onlyz.pilot == pilotzones. - Co-op (gated off): turret bodies are only drawn for pilot 0 –
main.gdsyncsturret_bodiesfromsim.turret(P1’s arsenal); a P2 turret fires from an invisible unit. - Tutorial walls should look like the Survival arena walls (story-map walls vs
ArenaBorder). ✅ DONE (4dd6c33a, branchtutorial-walls) — walls/sealed gates/false walls drawn by the sharedborder_look.gdshaderinc(union SDF, one MultiMesh); Debug Settings “Tutorial Walls (A/B)”; capturesdocs/captures/tutorial-walls-2026-10-02/. Needs Chris’s eye + ATV frame-time check. - Chalice pools feel too small; make them bigger and look better (Chris, 2026-10-07).
✅ DONE (branch
chalice-pools, not merged) — bible area 90 -> 130 (a real DPS buff, ~2.1x area, not compensated) + liveWeaponChalice.RADIUS_MULT;ChalicePoolLookliquid pool behind Debug Settings “Chalice Look (A/B)”; half-res, crowd/QM-L2 lite, optionalPerfCuts.chalice_lite(off). M5: +0.5-0.7 ms for 8 pools vs the hexagon’s ~0.1 (rendering-and-performance.md “Chalice”). Needs Chris’s eye in motion (stacked evolved pools are bright) + ATV frame-time check. Also fixed there: each pool poisons with its own cast element, not P1’s (co-op).
- Orbit Shards VFX stays on screen after the weapon is removed via the Remote Console (bug).
✅ DONE (
-
Rift Bolt detonation (2026-10-12) ✅ DONE (merge rift-detonation): every weapon hit adds an element stack (cap 6); a Rift Bolt hit detonates them (bolt dmg × 0.25 × stacks, half splash within 70 px, bosses half). Rift Bolt base damage 6.0 → 5.25 (calibration:
docs/research/2026-10-12-rift-bolt-detonation-calibration.md). Specdocs/superpowers/specs/2026-10-12-rift-bolt-detonation-design.md.- Chris: on-device feel-tune (DETONATE_, RINGS_MAX 3 vs 6, RIFT_DETONATE_SFX_).
- Chris’s call: Chalice pools + Orbit add a stack every tick (cap in ~0.1 s) — keep, or add a per-source stack interval?
- Measure 6 aura rings × full swarm on Apple TV (frame time); RINGS_MAX 3 is the live fallback.
- Chris’s call: tut_riftbolt detonation line plays while other guns are off (no detonation seen there).
- Start a new learning-autopilot persona epoch (AUTOPILOT_VERSION 1 → 2) — needs Chris’s OK.
- Detonation SFX has no cooldown (pierce through a stacked crowd fills PER_FRAME_CAP).
3c · Remote-control surface — the no-resubmit program (Chris, 2026-10-01)
Section titled “3c · Remote-control surface — the no-resubmit program (Chris, 2026-10-01)”Goal: the next submission (1.0.1) should carry everything that lets us change the game WITHOUT another App Store review. The 1.0 build (BUILD 206) carries only remote-config batch 1 (12 tunables + the bible overlay,
remote-config/manifest/build-206.json) — everything below applies from 1.0.1 (BUILD 249,remote-config/manifest/build-249.json, 494 tunables) on. Corrected 2026-10-02. Governing constraint: Apple 2.5.2 — download DATA only, never executable code (no GDScript, no shaders). Reference:docs/architecture/remote-config.md.
Already built, ships with 1.0.1, zero extra work:
- 84 sim tunables (
remote-config/manifest/build-230.json) + every numeric bible field viaBibleOverlay(weapon/enemy/boss stats, upgrade/mod magnitudes, shop prices + cost growth). - Rollback (publish
{}), per-channel patches (--channel testflight), the 30 s crash-guard blacklist, live-event windows and A/B cohorts are Worker-only changes (client already sendsinstall_idand polls/configat boot/menu/resume/run-start).
Work queue for this program (each its own chunk, in order):
- RC-1: Boolean feature flags / kill switches — ✅ DONE (
34873ecc)."type": "bool"registry entries ride the same fetch/reset/apply/config_hashpipeline (no bounds); first flags:Sim.DIMENSIONS_ENABLED(boss-death dimension offers) andDamageNumbers.ENABLED(render-side — entries may now point outsidesim/). publish-config validates true/false only, the manifest types them"bool", the liveness probe flips the bundled default. A new flag is now a one-line registry entry + a guard at the consumer. - RC-2: Maintenance / min-build banner — ✅ DONE (
f1c94c6e; Worker deployed 2026-10-01, version179643e1). The Worker merges a globalnotice(KV keynotice) into every/configresponse — including builds with no patch; the start menu shows it as a glass banner (cyan info / amber warn), filtered per build viamin_build/max_build. Publish:node scripts/publish-notice.mjs(--clearremoves). Comms, not config: excluded fromconfig_id/crash-guard/config_hash; a notice failure never breaks a patch. - RC-3: Remote strings — ✅ DONE (this commit). Display-copy bible strings are overlayable
via
BibleOverlay.STRING_FIELDS(weapons.name,enemies.name,mods.name,meta_upgrades.name,meta_upgrades.desc) — non-empty, single-line, ≤200 chars, stamped intoconfig_hash. Machine-key strings (mods.effect,weapons.element, …) stay unpatched on purpose. Manifest regen: 636 bible fields. UI chrome strings (menu labels etc.) remain hardcoded — add to the whitelist only when a real rename need appears. - RC-4: Render-side tunables — ✅ DONE (this commit).
QualityManager.ENABLED(bool flag — a patch can now switch the adaptive ladder on remotely; replacesmain.gd’sADAPTIVE_QUALITYconst) plus the ladder thresholds/timings (DOWN_FPS,HARD_DROP_FPS,DOWN_DWELL,PROBE_DWELL,PROBE_BACKOFF,SETTLE,WARMUP,EMA_ALPHA) andNovaBurstLook.LIFE_EVOLVED. NewCROSS_RULESpairHARD_DROP_FPS <= DOWN_FPS, mirrored inpublish-config.mjs. Liveness viatests/test_render_remote_config.gd(the probe can’t see render-side values). Glow/bloom and particle budgets deliberately deferred — no incident history, and the ladder flag already covers the “strip fidelity remotely” case. (docs + tests in this chunk) - RC-5: Remaining sim tunable batches ✅ DONE 2026-10-01 (skill
bh-tunable-batch): enemy behaviours/attacks, drone director, rest ofsim.gd— all three batches shipped. Batch 6 (rest ofsim/*.gd) ✅ DONE 2026-10-01 (1df3b4f5, mergedf68ce4f0, manifest build-236 = 463 tunables): 186 keys shipped across Sim/SpawnDirector/bosses/DimensionHazards etc., 19 reverted dead (4 BossWarden tutorial/story-only paths, FUNZO_ZONE_DURATION + LASER_ACTIVE_S visuals-only, the rest listed in the remote-config doc), 180 probe-LIVE + 6 wiring-pinned, 10 new CROSS_RULES. Everything remotely changeable now is. Batch 4 (enemy behaviours/attacks) ✅ DONE 2026-10-01 (05b9a025, merged5d523a05, manifest build-236 = 195 tunables): 92 keys shipped acrossEnemyBehaviors/EnemyAttacks/EnemyChipFire, 9 reverted dead (CUSTOM_*×8 dev-console-only,ELITE_ZAPPER_SPREADrosterless), 7 new CROSS_RULES, 30 probe-LIVE + 62 wiring-pinned (probe scenario-blindness documented — a roster/wave-pace probe override is the clear next tooling investment). Batch 5 (drone director) ✅ DONE 2026-10-01 (7ed3930e, merged, manifest build-236 = 235 tunables): all 40 drone keys shipped, 0 reverted; probe gained--meta-levels/--drone-loadoutflags so runs field all four drone classes (38/40 probe-LIVE, 2 overcharge-heal keys wiring-pinned). - Balance bug (found by the batch-5 liveness work): a bought Logistics drone overcharge never fires without the endurance upgrade — its 8 s cooldown equals the drone’s 8 s life. Cooldown and life are now both remote tunables, so this can be patched without a build once Chris picks the intended values.
- RC-6: Remote URLs — ✅ DONE (this commit), rescoped on a finding: the game binary has NO
hardcoded user-facing links to un-hardcode (the only hardcoded URLs are the Worker endpoints
themselves, which can’t be remote-served — bootstrap problem; store-listing links live in ASC
metadata, changeable without a build). What shipped instead: the notice banner’s
urlfield (validated by publish-notice.mjs since RC-2 but dropped client-side) is now carried end-to-end —notice_for_buildkeeps https-only urls and the start menu renders a tappable CTA line (Platform.can_open_links(); hidden on tvOS, which has no browser). Discord/support/privacy CTAs are now publishable with any notice. A generallinksmap was deliberately not built (no consumer = dead knob; rebuild on this channel if an about/links UI ever lands). - RC-7 (deferred, big): R2 asset pipeline — download+cache+fallback for textures/audio
(data, so 2.5.2-legal): seasonal reskins, new music, texture-swap VFX variants without a build.
Design sketch (2026-10-01, for whoever picks it up):
- Manifest: a KV-served JSON (same Worker, e.g.
assetskey merged likenotice) listing optional packs:{id, version, sha256, bytes, target}per file. Versioned like config_id. - Client: after the config fetch, diff the manifest against a
user://assets/cache; download missing/outdated files over HTTPS from R2, verify sha256 BEFORE use, and only then swap. Any failure (offline, bad hash, partial) silently keeps the bundled asset — the game must ship complete and never depend on a download. - Consumers opt in per call site: a texture slot (arena background variant, VFX sprite) or
music track asks
AssetCache.get(id)and gets the remote file if verified, else the bundled fallback. No manifest entry = bundled, always. - Hard rules: data files only (no .gd/.gdshader/.tres with script refs — 2.5.2); a size
budget + cache eviction (iOS cellular); never block boot or a run start on a download;
downloads reported via
EventQueue.push(a new data kind → privacy page + ASC label update). - Why deferred: no consumer needs it yet (no seasonal content, no alternate soundtrack), so it would ship unexercised — build it when the first real asset swap is wanted, not before.
- Manifest: a KV-served JSON (same Worker, e.g.
- RC-8: Remote background theming ✅ DONE (
ac83ebd2) (Chris, 2026-10-01: “can backgrounds be changed this way” — scope: FULL theming). All backgrounds are procedural, so no RC-7 downloads needed: (a) numeric knobs (parallax, densities, counts, scales, alphas, speeds) via the RC-4 render-tunable pattern; (b) variant choice — already data-driven (set_variant_by_name) and live-switched by Debug Settings — a remote int key (-1 = AreaDefs default, 0-4 = force Grid/Starfield/Nebula/Aurora/Galaxy); (c) a NEW"type": "color"in the tunables pipeline (hex string in the JSON patch →Color.htmlclient-side, publish-config validation, manifest typing) covering: ArenaBackground globals (grid lines, border, starfield, galaxy core/outer), NebulaBgcol_lo/col_hi+ intensity (mind the HDRpow(1/2.2)pre-compensation trap, nebula_bg.gd:78-85), per-area Dimension tints (AreaDefs.tintoverrides), aurora band hues. Never remote-able: the shader math itself (FBM/twinkle/aurora-wave code — 2.5.2 + needs a build). Full inventory: this session’s explorer report (per-system knobs with file:line).
Hard limits — no submission can buy these: new content needing code wiring (new
weapons/enemies/bosses/mechanics, adding/removing bible entries); downloaded GDScript/Lua/shaders
(2.5.2 violation); structural fields (enemies.*.radius, meta_upgrades.*.max_level, bundled-0
fields, const-folded/match-gating consts — full table in remote-config.md); beyond the 0.1×–10×
tunable bounds; privacy-significant collection changes (ride a submission + the ASC label).
Outside the binary — App Store Connect, no new build ever needed: promotional text (anytime, no review), price/territories/phased release, In-App Events, custom product pages. Screenshots / description / keywords ride a version review.
4 · Release steps
Section titled “4 · Release steps”- Bump
application/short_versionto1.0.1in the tvOS + iOS presets (only after Apple approves 1.0 — a rejected version is reused, not bumped). - Tag the shipped 1.0 commit once approved.