Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 221 additions & 0 deletions .agents/skills/repack-debug-runtime/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
---
name: repack-debug-runtime
description: >
Use when inspecting a running Re.Pack app or dev server to find out why
something fails at runtime: chunks or scripts not loading (404, timeouts,
cache), Module Federation remotes/shared packages misbehaving, compilation or
HMR errors, LogBox stacks that are not symbolicated, or uncaught JS errors.
Covers starting the tester apps with the Re.Pack DevTools plugin, the
`rozenite agent` tools of `callstack/repack-devtools`, the
`/repack-devtools/*` dev-server endpoints, and playbooks per failure type.
---

# Debug a running Re.Pack app

`packages/repack-devtools-plugin` (private, workspace only) records what
Re.Pack does at runtime and on the dev server, computes diagnostics, and
exposes them two ways:

- **App side** (needs a running app): `rozenite agent callstack/repack-devtools <tool>`
- **Dev-server side** (works without an app, even when the bundle fails to
build): `curl <origin>/repack-devtools/state`

Both are wired into every tester app behind `WITH_ROZENITE=true`. Never touch
ports or devices you did not start: other sessions often own `8081` and
`emulator-5554`.

## 1. Start

```sh
cd apps/tester-app # or tester-federation, tester-federation-v2, tester-expo
WITH_ROZENITE=true pnpm start --port 8091 # MF testers: start:hostapp / start:miniapp
```

The log must show `[Rozenite] Loaded N plugin(s)` including
`@callstack/repack-devtools-plugin`. Check the endpoint before launching the app:

```sh
curl -s localhost:8091/repack-devtools/state?requests=none
```

Point a debug build at the port (Android; package ids are in each `app.json`):

```sh
adb -s <serial> reverse tcp:8091 tcp:8091
adb -s <serial> shell "run-as <pkg> sh -c 'mkdir -p shared_prefs && echo \"<?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\" standalone=\\\"yes\\\" ?><map><string name=\\\"debug_http_host\\\">localhost:8091</string></map>\" > shared_prefs/<pkg>_preferences.xml'"
adb -s <serial> shell am force-stop <pkg>
adb -s <serial> shell am start -n <pkg>/com.microsoft.reacttestapp.MainActivity
```

MF remotes have fixed URLs in the host config (`localhost:8082`, `8083`); also
`adb reverse` those ports. On iOS simulators, `localhost` already reaches the
host; use the dev menu or `RCT_jsLocation` to change the port.

## 2. Inspect

Run from an app directory so `rozenite` resolves (`pnpm exec rozenite …`).
Pass `--port <dev server port>` to every call.

```sh
pnpm exec rozenite agent targets --port 8091 # app connected?
pnpm exec rozenite agent callstack/repack-devtools get-overview --port 8091
pnpm exec rozenite agent session stop --port 8091 # when done
```

**Always start with `get-overview`.** It returns app info (platform, RN
version, bundle URL, public path, dev server), record counts, and
severity-sorted `diagnostics` merged from the app and the dev server. Every
diagnostic has a `ref` that tells you which tool gives details:

| `ref` | Follow up with |
| --- | --- |
| `<script uniqueId>` | `get-script --uniqueId <ref>`, `get-script-manager` |
| `call:<seq>` | `list-script-calls --uniqueId <id>` |
| `fault-<n>` / `server-fault-<n>` | `list-faults` (injected by you; not a real bug) |
| `federation-event:<seq>` | `list-federation-events --errorsOnly true` |
| `<instance>:<scope>/<pkg>` | `get-federation` |
| `err-<n>` | `get-error --id <ref>`, then `symbolicate --errorId <ref>` |
| `request:<seq>` | `get-dev-server --requests failed` |
| `compilation:<platform>` | `curl <origin>/repack-devtools/compilation?platform=<platform>` |

### Tools

Read-only: `get-overview [--devServer false]`, `list-scripts [--status failed] [--search x]`,
`get-script --uniqueId`, `list-script-events [--uniqueId] [--since seq]`,
`get-federation`, `list-federation-events [--type] [--errorsOnly true]`,
`list-errors [--source global-handler|script-manager|federation]`, `get-error --id`,
`symbolicate [--stack "<raw stack>"] [--errorId]`,
`get-dev-server [--origin http://localhost:8082] [--requests failed|all|none]`,
`list-script-calls [--uniqueId] [--method loadScript] [--errorsOnly true]`,
`get-script-manager`, `list-faults [--origin]`.
List tools paginate with `--limit` and `--cursor` (take the cursor from `page`).

Actions, to reproduce issues without driving the UI:
`load-script --scriptId <id> [--caller <id>]`, `prefetch-script`,
`invalidate-scripts [--scriptIds a,b]`, `load-remote --id Remote/Module [--instance]`,
`clear [--scope scripts|federation|errors|all]`, `reload`.

Fault injection, to test error handling and recovery without editing app code:

- `inject-script-fault --stage resolve|load [--scriptId x] [--caller y]
[--action fail|delay] [--delayMs n] [--times n] [--code c]`: through
ScriptManager hooks. `resolve` throws before resolvers run (a resolver that
fetches a manifest while offline); `load` throws before the download with
`NetworkFailure` (loadScript only).
- `inject-server-fault --match <substring|/regex/> [--status 503]
[--delayMs n] [--drop true] [--times n] [--platform] [--origin]`: on any
dev server wrapped with `withRepackDevTools`, including MF remotes. Faulted
responses carry `fault` in the request log; dropped connections are logged
with `status: 0`.
- `clear-faults [--id] [--origin]`. Faults stay active until `--times` is
used up; always clear them when done, and remember that the overview's
`faults` diagnostics mean failures you caused yourself.

### Dev-server endpoints (curl, no app needed)

- `GET /repack-devtools/state?requests=failed|all|none`: per-platform
compilation (`status`, `builds`, errors, warnings, assets, changed files),
request log (failed requests keep the first 4 KB of the response body), and
diagnostics.
- `GET /repack-devtools/compilation?platform=android`: one compilation in full.
- `POST /repack-devtools/clear`: empties the request log.
- `GET|POST|DELETE /repack-devtools/faults[?id=]`: list, add (JSON body like
`inject-server-fault`) or remove server faults.

Every MF dev server has its own endpoint (host and each mini app).
`builds: 0` with `status: compiling` means that platform was never requested.

## 3. Playbooks

**Chunk / script fails to load.** `list-scripts --status failed`, then
`get-script`. `error.stage` says where it broke (`resolve` = no resolver
matched or the resolver threw; `load` = download or evaluation;
`create` = bad locator). Compare `locator.url` with the dev server: a 404
diagnostic lists emitted assets with similar names. If the record shows
`source: cache`, call `invalidate-scripts` and `load-script` again to tell
cache problems from server problems.

**Retry / recovery after a failure.** Inject the failure once
(`inject-script-fault --stage resolve --scriptId X --times 1`, or
`inject-server-fault --match X --status 503 --times 1`), call `load-script`
twice, then `list-script-calls --uniqueId X`. A healthy retry shows
`resolverRan: true` on the second call. `resolverRan: false` with
`joined: rejected load` means ScriptManager returned the stored failure
instead of trying again; `get-script-manager` then lists that promise as
`rejected` (issue #1506). The same check works for prefetch-then-load and for
concurrent callers (`joined: pending ...` is expected deduplication).

**Resolver and cache behaviour.** `get-script-manager` lists resolvers with
priorities, taps per hook and cache entries. `list-script-calls` tells how
often resolvers really ran versus calls served from a stored promise; compare
with the dev server request log to see what was actually downloaded.

**Module Federation.** `get-federation` shows each host instance's remotes
(`loaded`), shared packages per scope with every registered version, who
provided it (`from`), and which one is `loaded`. Diagnostics flag singletons
loaded twice and loaded versions that miss a `requiredVersion`. For load
failures, `list-federation-events --errorsOnly true` gives the hook,
remote entry URL and error. Then check the remote's own dev server:
`get-dev-server --origin http://localhost:8082 --requests failed`.
Use `load-remote --id MiniApp/<exposed>` to retry a single remote load.
If `runtimePluginInstalled` is false, MF events are not recorded: add
`require.resolve('@callstack/repack-devtools-plugin/mf-runtime-plugin')` to
`runtimePlugins`, or call `installRepackDevTools()` in the entry.

**Compilation or HMR broken.** Use curl, since the app may not load.
`compilations[].errors` carry ANSI-stripped messages with module names, and
`changedFiles` lists what triggered the last rebuild.

**Unsymbolicated stacks / `/symbolicate` 400.** Run `symbolicate` (no args
throws and symbolicates a probe error in the app) or
`symbolicate --errorId err-N`. Frames with `unmapped: true` still point at a
bundle URL; `sourceMaps[]` gives the HTTP status of each bundle's `.map`.
Re.Pack's `/symbolicate` only keeps frames whose `file` is an http(s) URL, and
answers 400 "Cannot infer platform" when none carries a platform (query
`platform=` or `name.<platform>.bundle`).

**Uncaught errors.** `list-errors`, `get-error --id`, then
`symbolicate --errorId`.

## Gotchas

- **Agent tool calls time out after about 30 s**: a `load-script` that waits
on a long injected delay times out on the agent side while the app keeps
going. Read the outcome afterwards with `list-script-calls`.
- **`resolverRan` reads as "ScriptManager reached its resolve step during
this call".** It is false when an injected `resolve` fault fired (check the
`faults` column) and for calls that only joined another attempt (`joined`
set).
- **MF manifest errors hide the HTTP status**: MF parses error pages as JSON,
so a 4xx/5xx shows up as `JSON Parse error`. The `afterLoadManifest` event
`detail` has `HTTP <status>`.

- **Startup is only captured** when the entry calls
`installRepackDevTools({ boot: true })` before other code
(`app.bootRecording: true`). The testers do this.
- **After a reload the agent session goes stale**: tool calls time out with
`Tool call timeout` or `ReactNativeApplication.enable timed out`. Run
`rozenite agent session stop` and retry; the new runtime starts with empty
records.
- **Rebuilding the plugin package while a dev server runs** (`pnpm build` wipes
`dist` first) leaves the server's resolver with a stale
`Cannot find module '@callstack/repack-devtools-plugin'`. Restart the dev
server, then relaunch the app.
- **"More than one device is connected"**: every app pointed at the port
(including a previous tester left running on the same emulator) is a
target. Pass `--device <id>` from `rozenite agent targets` or set
`ROZENITE_DEVICE`, or force-stop the other app.
- **`Unknown domain "callstack/repack-devtools"`** means the app has not
called `useRepackDevTools()` yet: it crashed or is stuck before the first
render. Read `rozenite agent console getMessages --limit 40` and take a
screenshot.
- Launch Android apps with
`adb shell am start -n <pkg>/com.microsoft.reacttestapp.MainActivity` for
the testers; `monkey` launches are unreliable.
- Ignore the `/_expo/*` and `/json*` requests made by DevTools; the request log
already filters them.
- `rozenite agent domains` lists only built-in domains on some versions. Call
`rozenite agent callstack/repack-devtools` to list the plugin's tools.
- Rozenite's own docs: `pnpm exec rozenite skills list` / `skills show <name>`.
- From scripts, use `@rozenite/agent-sdk` with the typed descriptors in
`@callstack/repack-devtools-plugin/sdk` (`repackDevToolsTools.getOverview`).
92 changes: 92 additions & 0 deletions .agents/skills/repack-reproduce-issue/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
name: repack-reproduce-issue
description: >
Use when asked to reproduce, root-cause or fix a Re.Pack bug report (GitHub
issue, crash log, "X doesn't work with Re.Pack") end to end and unattended:
pick the right tester app, reproduce the failure on a device or simulator,
gather evidence with the Re.Pack DevTools agent tools, find the root cause in
packages/, fix it, and add a regression test.
---

# Reproduce and root-cause a Re.Pack issue

Work through these steps in order and keep notes of every command and its output.
The final report must say what was observed, not only what was inferred.
Runtime inspection is covered by the `repack-debug-runtime` skill; load it
alongside this one.

## 1. Pin the report down

Extract: platform(s), RN version, bundler (Rspack/webpack, `USE_WEBPACK`),
Module Federation version (none, v1, v2), dev vs release, Hermes, and the exact
symptom (error text, stack, which screen). If the report has a repro repo,
read its config before touching ours.

## 2. Pick the closest tester

| Area | Tester |
| --- | --- |
| Async/remote chunks, ScriptManager, assets, NativeWind, Reanimated | `apps/tester-app` |
| Module Federation v1 (`Federated`, `ModuleFederationPluginV1`) | `apps/tester-federation` |
| Module Federation v2, manifests, `preloadRemote`, runtime plugins | `apps/tester-federation-v2` |
| Expo modules / expo-router / `@callstack/repack-expo` | `apps/tester-expo` |
| Pure bundling/resolution, no device needed | `tests/integration`, `tests/resolver-cases`, `tests/metro-compat` |

Prefer a no-device reproduction when the bug is in compilation or resolution:
an integration test or a `pnpm bundle:<platform>` run is faster and deterministic.

## 3. Reproduce

1. `pnpm install && pnpm build` at the root (stale `dist` output causes false
failures).
2. Pick free ports and your own device. Do not reuse a device or port that
another session is using (`adb devices`, `lsof -iTCP -sTCP:LISTEN`).
3. Start the dev server(s) with `WITH_ROZENITE=true` and launch the debug build
(see `repack-debug-runtime` §1). Build native apps once per RN upgrade:
`cd android && ./gradlew assembleDebug`, or `pnpm ios` for the simulator.
4. Make the minimal code/config change in the tester that triggers the bug.
Keep it as a separate commit so it can be dropped or turned into a test.
For failure and recovery bugs (offline, flaky CDN, 5xx, timeouts, a
resolver that throws) try `inject-script-fault` / `inject-server-fault`
first: they need no app change and work on every tester.
5. Trigger it. Prefer agent tools over UI driving:
`load-script`, `prefetch-script`, `invalidate-scripts`, `load-remote`,
`reload`. When the bug needs UI interaction, drive the device with
`agent-device` if it is available, or with `adb shell input`.
6. Confirm the failure: `get-overview` must show the diagnostic that matches
the report, or `list-errors` must show the reported error. Take a
screenshot (`adb exec-out screencap -p`) when the symptom is visual.

If it does not reproduce, record the environment differences (versions,
config, platform) and stop guessing; ask for the missing detail or try the
reporter's repro.

## 4. Find the root cause

- Follow each diagnostic's `ref` to the raw record (`get-script`,
`get-federation`, `list-federation-events`, `get-dev-server`).
- Compare app-side and server-side evidence. A script 404 plus a
"similar asset" hint means a URL/filename mismatch; a 200 from the dev server
plus a ScriptManager `load` error means evaluation or native-side failure.
- Map the failure to source in `packages/repack/src` (ScriptManager, plugins,
loaders) or `packages/dev-server/src` (routes, symbolication). Read
`agent_context/` for the area first.
- State the root cause as a falsifiable claim, then verify it: revert or patch
the suspected line and show the symptom disappears and reappears.

## 5. Fix and lock it in

1. Fix in `packages/`, not in the tester (shipped code must not depend on
monorepo layout).
2. Add a regression test next to the code (`vitest` in the package) or in
`tests/integration`. It must fail without the fix.
3. Re-run the reproduction: the diagnostic is gone and the feature works on
device. Run the package tests plus `pnpm typecheck` and `pnpm lint`.
4. Add a changeset for published packages (`pnpm changeset`).

## 6. Report

Include: environment (device, ports, versions), the repro steps as commands,
the evidence (tool outputs trimmed to the relevant fields), root cause with
file and line, the fix, and the test. Clean up: stop dev servers you started,
`rozenite agent session stop`, and shut down emulators you created.
1 change: 1 addition & 0 deletions .claude/skills/repack-debug-runtime
1 change: 1 addition & 0 deletions .claude/skills/repack-reproduce-issue
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,6 @@ packages/**/docs

# watchman
.watchman-cookie*

# Rozenite plugin build intermediates
.rozenite/
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ All development is done directly on GitHub, and all work is public.

To get extra [Rozenite](https://www.rozenite.dev) panels (network activity, plus navigation and storage in the Module Federation hosts) in React Native DevTools while testing, start a tester app's dev server with `WITH_ROZENITE=true`, e.g. `WITH_ROZENITE=true pnpm start`.

This also enables the workspace-only Re.Pack DevTools plugin (`packages/repack-devtools-plugin`): panels for ScriptManager, Module Federation, the dev server and symbolication, the same data as `rozenite agent callstack/repack-devtools` tools, and `GET /repack-devtools/state` on every dev server. The `repack-debug-runtime` and `repack-reproduce-issue` skills in `.agents/skills` describe how to use them.

### Commit message convention

We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:
Expand Down
1 change: 1 addition & 0 deletions agent_context/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ works, and is kept in sync with the implementation as it evolves.
| Folder | Topic |
| --- | --- |
| [rspackv2-jul2026](./rspackv2-jul2026/design.md) | Dual Rspack 1.x/2.x support |
| [repack-devtools-oct2026](./repack-devtools-oct2026/design.md) | Re.Pack DevTools plugin: runtime/dev-server diagnostics for panels and agents |
Loading
Loading