Skip to content

App Clip (Warden demo) + Beta skin — design

App Clip (Warden demo) + Beta skin — design

Section titled “App Clip (Warden demo) + Beta skin — design”

Status: approved in brainstorm with Chris, 2026-10-14. Target: 1.2 (an App Clip ships inside an app version’s build, so it lands with whichever version it is ready for; not 1.1, freeze 10-17).

Retargeted to 1.1 under a hard drop rule (Chris, 2026-10-15): if the clip is not green on Chris’s iPhone by the 2026-10-17 feature freeze it comes out of 1.1 and follows in 1.1.1. The “Target: 1.2” line above is superseded.

The web demo already offers “try before install”, but a native clip feels like the game is already installed: polished, full-speed Metal, haptics, and a one-tap install that continues straight into the full game. The clip is a short spectacle — swarm, rapid power growth, Warden fight — that ends on a “continue playing” card.

In this spec:

  1. The App Clip — Warden run, end card, install overlay, “Beta skin earned” handoff flag.
  2. The Beta skin — a cosmetic skin layer in the full app, four candidate looks to A/B, unlocked from the clip flag.
  3. Branded launch screen + boot splash — shared by the full app and the clip. Independent of the clip; can ship earlier (e.g. 1.1) on its own.

Out of scope (follow-on spec): Game Center in the full app, with unlocks (Beta skin included) associated to the player’s Game Center id server-side so they follow the player across devices. Builds on the account-shaped cloud-save backup (2026-10-04-cloud-save-backup-design.md). Game Center is NOT used inside the clip (friction in a 2-minute session; GameKit availability inside App Clips unverified).

Later, once internet multiplayer is live: Beta nameplate badge, Beta drone colour, Beta-only spawn-in warp effect.

  • A copy of the iOS shell converted to an App Clip target (product type com.apple.product-type.application.on-demand-install-capable, entitlements com.apple.developer.on-demand-install-capable + parent-application-identifiers, NSAppClip plist dict, iOS 17.0 deployment target) built, signed, installed and booted on iPhone 15 Pro Max (tutorial and menu both reached). Signing registered App ID digital.lumara.bulletheaven.dev.Clip.
  • Limit: 100 MB uncompressed, iOS 17+, digital invocation only (Safari banner, Messages, Maps, Spotlight, App Store). Physical invocations (QR/NFC/App Clip Code) cap at 15 MB except via the App Store Connect demo link.
  • Measured sizes (BUILD ~325):
    Part Full iOS build Clip estimate
    Engine binary (stripped; unstripped 79 MB) 54 MB 54 MB
    .pck 95 MB (60 music, 18.7 shader cache, 14.6 SFX) ~33-36 MB
    Frameworks (Sentry 7 MB + haptics) 7.3 MB ~0.3 MB
    Assets/plist ~1 MB ~1 MB
    Total ~89-92 MB
  • Shader cache is ~85% 2D CanvasShaderRD (used); only ~3 MB of 3D SceneForwardMobileShaderRD can go.
  • Headroom lever if needed later: a fork engine build with 3D disabled (shrinks the 54 MB binary). Optional, not a blocker.
  1. Launch — branded black launch screen with key art, seamless handoff to a matching engine boot splash. No grey, no Godot logo.
  2. No menu, no tutorial — straight into a 3-2-1 countdown on Cobalt. One controls hint line for the first 5 s.
  3. Ramp (~45 s) — dense swarm, ~3x XP, about 3 level-ups (was ~5x; Chris 2026-10-16: “way too many upgrades, need about 1/3”). Offers drawn ONLY from a curated showpiece pool (evolutions, Rift Bolt, Supernova, etc.) so every pick is a visible jump.
  4. Warden (~60-90 s) — spawn growl, boss theme, tuned so a typical player wins in ~75 s; losing is possible.
  5. Handoff, win or lose — “Warden destroyed. Beta skin unlocked.” / “So close. Beta skin unlocked anyway.” Then the “continue playing” card: full-game highlights (more bosses, areas, hulls) and Apple’s install overlay. The skin is granted on BOTH outcomes.
  6. Replay — secondary “Fight again”.

Total session ~2-2.5 min.

  • New export preset iOS-Clip in platform/tvos/export_presets.cfg with feature tag app_clip and a content-stripping exclude_filter (all music except boss_theme; audio/candidates; SFX not used by the clip run; Sentry addon; 3D shader cache if excludable).
  • main.gd on boot: OS.has_feature("app_clip") → _on_mode_chosen("clip") directly, bypassing the start menu, tutorial and survival intro.

ClipDirector (new, /sim, extends RefCounted)

Section titled “ClipDirector (new, /sim, extends RefCounted)”
  • Drives ramp → Warden spawn → end state; exposes phase and outcome for the render/UI to read.
  • Console tunables (registry + tools/tunable_docs/docs.d/*.json label + LiveScope hook, per bh-tunable-batch): clip_xp_mult, clip_ramp_s, clip_warden_hp_scale, clip_swarm_density.
  • Sim has no mode field today (main.gd holds _mode): the run setup attaches a ClipDirector to the sim only in clip mode; null everywhere else.
  • Curated offer pool: UpgradeSystem.roll_upgrade_choices filters to the clip pool when a ClipDirector is attached, still drawing from upgrade_rng.
  • Warden spawned through the normal spawn path with biomass_v passed explicitly (XP trap in root CLAUDE.md).
  • Everything gated on clip mode: survival/coop/determinism baselines must not move (pinned tests).

Native bridge: addons/dc_appclip (new GDExtension, built like dc_haptics)

Section titled “Native bridge: addons/dc_appclip (new GDExtension, built like dc_haptics)”
  • group_container_path() -> String for App Group group.digital.lumara.bulletheaven (both targets).
  • present_install_overlay() → SKOverlay with AppClipConfiguration.
  • iOS-only; excluded from every non-iOS preset.
  • Handoff file: <group>/clip_rewards.json { "beta_skin": true, "earned_at": <unix> }. Clip writes it at end state; the full app reads it on launch, grants the skin into MetaState, and marks it consumed (idempotent re-grant is harmless).
  • scripts/release/add-app-clip-target.sh adds the clip target to build-ios (product type, entitlements incl. App Group, NSAppClip dict, iOS 17 target, embeds the clip in the parent’s AppClips/, bundles the clip .pck, links the same libgodot.a). Idempotent; re-run after any full export, like apply-ios-metrickit.sh. Tracked inputs live in platform/tvos/patches/.
  • Parent app gains the App Group entitlement too.
  • Clip bundle ids: store digital.lumara.bulletheaven.Clip, Dev digital.lumara.bulletheaven.dev.Clip.
  • The clip build fails if the thinned clip exceeds 95 MB (App Thinning size report from the archive/export).
  • Skins are a NEW render-only concept, separate from hulls (hulls carry stats in sim/ship_bonuses.gd). MetaState.owned_skins: Array[String] + equipped_skin: String; a ShipSkin layer in the player renderer applied over any hull. Never touches /sim stats.
  • Four candidates behind a live console tunable ship_skin (0 off, 1-4):
    1. Prototype wireframe — glowing cyan edge lines over near-black fill, slow scanline sweep.
    2. Holo-film — oil-slick iridescence that shifts hue with banking; reuses the Supernova thin-film work.
    3. Glitch build — RGB channel split, brief corruption jitter on warp/hit; must not read as a bug.
    4. Void chrome — black mirror-chrome, single white-hot rim, faint violet afterimage trail.
  • Chris A/Bs on device; the winner becomes “Beta”. Soft/glow draws use covered saved materials with the hdr_alpha remap (root CLAUDE.md); review in both SDR and HDR.
  • Equip UI: a skin row in the hull picker (fit-tested at iPad/iPhone/TV profiles).
  • Replace the iOS Launch Screen.storyboard (currently grey 0.14 + Godot’s stock SplashImage) with a black background + Dark Cosmos key art; tracked copy in patches/.
  • Set application/boot_splash/* (image + black bg) in platform/tvos/project.godot to match, so the native → engine handoff is seamless. Also tvOS’s launch image.
  • GUT: clip-mode XP multiplier; offers restricted to the curated pool; Warden spawns at ramp end via the real spawn → kill → _sweep_dead path (awarded XP asserted); end state fires on win AND loss; handoff read/write through an injectable path; skin grant idempotent.
  • Determinism: the three pinned baseline tests stay green untouched.
  • Shaders (skins): windowed compile under mobile and gl_compatibility; baked iPhone Dev install check (A17 baker bug).
  • Device: App Clip local experience on iPhone (Settings → Developer → Local Experiences); fresh install via bh-fresh-install-stall-check; install-from-clip → full app grants the skin.
  • Size gate passes.
  • Curated showpiece pool contents (pick in implementation with Chris).
  • Beta skin winner (after the A/B).
  • App Store Connect: App Clip experience metadata (header image, subtitle, action “Play”), Safari Smart App Banner on darkcosmos site (~/Claude/bullet-heaven-site) — outward-facing, Chris’s OK.