platform/record-unreal/reference/grapple-live.md

Grapple live recording: reuse the successful path

Locate the existing implementation

In the owning checkout, inspect scripts/package/record.py and record_window.swift; recorder_command builds the Swift source when its hash changes. The retained SCK implementation is in /Users/james/dev/grapple-game-worktrees/delivery-successor/scripts/package/. Do not assume every checkout's record.py is the same version or copy another checkout's Unreal binaries. Read the current owner's handoff before changing it.

The recorder's interface is:

record-window PID WINDOW_ID SECONDS OUTPUT.mov RECEIPT.json

It pins process birth plus window ID, uses desktopIndependentWindow, derives pixel dimensions from contentRect * pointPixelScale, exports the first complete SCK sample to start-frame.png, and finalizes through native SCRecordingOutput. Direct executable launches need native process birth: NSRunningApplication launchDate can be absent.

Setup, then record

  1. Reuse the whole known-working route/launcher, including its parcel bypasses, paired key-down/key-up and identity-bound cleanup. Confirm real game readiness; an Entry window or issued macro is not gameplay. Neutral scripted axes do not isolate physical mouse input. Check the actual capture mode after possession: SetInputMode(GameOnly) can override launch-time NoCapture. Distinguish game-window focus from application activity.
  2. Read the exact owned window bounds first. If correct, skip menu exploration. When setup is needed, use allowed CUA control of that exact game/editor app, then read back the actual window and bounds. Activation returning true does not prove visibility. An app-specific CUA denial must not be bypassed; it does not establish that allowed game-window control is unavailable.
  3. Start the existing bounded SCK recorder. Inspect its first complete frame, validating the PNG against configured_dimensions, not a fixed 1280x720. Do not require screencapture -x -l to succeed before an SCK stream can run.
  4. Keep the recorder's 10-second no-complete-frame protection. Do not add a separate short operator-acknowledgment timeout: it expired during a valid inspection. Before input, check the recorder is alive and enough of the bounded take remains for the route and tail; otherwise submit no input.
  5. Retain native action/phase observations, window identity/geometry monitoring, original movie and finalization result. Let the recorder finish while the game remains alive, then use the existing owned cleanup. Inspect the actual action and return in the movie. A successful encode cannot override missing geometry monitoring or a failed gameplay assertion.

Full-screen/occluded setups have produced idle-only SCK samples; Mission Control has produced suspended updates and a multi-second held image. Retain those as capture failures, without guessing who changed the desktop. A later good take is not proof every desktop transition is safe.

Packaged input and audio

For macro input, reuse two_player.py's container path: ~/Library/Containers/<bundle>/Data/tmp/grapple-packaging/<take>/<role>/Binaries/current.txt. The scheduled exec path must equal the file actually written. UE's ExecMacro prefixes a path that lacks Binaries with ../../Binaries/, even when the supplied path looks absolute. Retain native command receipts and actual movement states; writing W/Space commands alone proves neither walking nor landing.

The retained audio variant sets capturesAudio=true and captureMicrophone=false; the screen-only recorder does not supply audio by default. Reuse that variant when audio is requested. Decode the muxed audio and confirm the actual cue occurs in the captured interval, keeping clock-alignment uncertainty explicit. An idle silent track is inconclusive; nonzero PCM proves output, not sound quality. Listen before calling a static/distortion fix accepted.

Retained evidence and limits

Use these receipts for unchanged setup evidence, not as acceptance for a new mechanic. James can judge the published visual result; technical route/capture checks remain with the owner. No extra native run is needed to document this path.