---
name: bombus-test-mcap-locally
description: >
  Load a local .mcap (or .bag) file into the coScene/bombus studio dev build to test
  video panels, playback, and timeline seeking without a cloud data source. Stands up a
  range+CORS file server and the dev server, and builds the remote-file load URL. Use when
  asked to "test a local mcap", "preview this recording/bag locally", "load my mcap in the
  studio", or to debug video decode / scrubbing in bombus.
---

# Test a local MCAP in the bombus studio

The studio's `remote-file` data source fetches over **HTTP range requests** and is
**cross-origin** from the dev server. So the file must be served by something that supports
ranges AND **exposes the range headers via CORS**. A plain `http-server --cors` or
`python -m http.server` fails with *"Support for HTTP Range requests was not detected"*
because the browser can't read `Accept-Ranges` cross-origin without
`Access-Control-Expose-Headers`. That's the whole reason for the bundled `serve-mcap.mjs`.

## Steps

1. **Serve the file** (range + CORS-expose) using the script in this skill folder:
   ```
   node ~/.claude/skills/bombus-test-mcap-locally/serve-mcap.mjs <dir-with-the-mcap> 8081
   # e.g. node ~/.claude/skills/bombus-test-mcap-locally/serve-mcap.mjs ~/Downloads 8081
   ```
   Run it in the background (it's long-lived).

2. **Start the dev server from the checkout you're testing** (it builds whatever cwd it's
   launched from — important when there are multiple worktrees):
   ```
   yarn web:serve      # binds :8080  (if yarn errors about a missing registry-token env var, set it to any dummy value)
   ```
   Confirm it's serving the intended checkout via its log line:
   `Content not from webpack is served from '<dir>/web/.webpack'` — `<dir>` must be your
   worktree, not another checkout.

3. **Open** the studio pointed at the file (URL-encode `ds.url` if the browser mangles it):
   ```
   http://localhost:8080/?ds=remote-file&ds.url=http://localhost:8081/<file>.mcap
   ```

4. In the **3D / Image panel**: set **Topic** to the video topic (e.g. `…/compressed/h264`)
   and **Calibration** to **None**.

## Gotchas

- **CORS expose headers** — the reason `serve-mcap.mjs` exists. Sanity-check it:
  ```
  curl -s -D- -o /dev/null -r 0-9 -H "Origin: http://localhost:8080" \
    http://localhost:8081/<file>.mcap
  ```
  Expect `206 Partial Content` + `Access-Control-Expose-Headers: Accept-Ranges, …`.

- **Wrong checkout** — `yarn web:serve` serves its cwd. With several worktrees it's easy to
  build the wrong one. Check the listener's working dir:
  ```
  lsof -a -p "$(lsof -ti:8080 -sTCP:LISTEN | head -1)" -d cwd
  ```

- **Port fallback** — if :8080 is already taken, the dev server silently binds **:8082**
  (and you'll wonder why your changes don't show). Free it first and start the two servers
  sequentially:
  ```
  kill "$(lsof -ti:8080 -sTCP:LISTEN)" 2>/dev/null
  ```

- **Branch push** — worktree branches here sometimes track `origin/main`; push with an
  explicit refspec (`git push origin HEAD:refs/heads/<branch>`) so a bare push can't go to
  main.

## Verify / debug video

- Scrub the timeline — the panel should land on the exact frame (not snap to a keyframe,
  not show the end frame).
- For GOP-seek debugging, the seek-backfill path can log `[gopBackfill]` lines (window
  bounds, collected frame count, resulting GOP length) — handy for confirming the GOP is
  bounded to the seek target rather than running to EOF.
