Canonical: https://rom-weaver.com/docs/development

# Development guide

rom-weaver is a Rust workspace with a native CLI and a React webapp backed by a threaded WASI module. [mise](<https://mise.jdx.dev>) pins the Rust, Node.js, and Binaryen versions and exposes the repository's build and test tasks.

<!-- START doctoc -->
## Table of contents

- [Prerequisites](<#prerequisites>)
- [Clone and bootstrap](<#clone-and-bootstrap>)
- [Build and run the webapp](<#build-and-run-the-webapp>)
- [Build and run the native CLI](<#build-and-run-the-native-cli>)
- [Test and lint](<#test-and-lint>)
- [Generated files](<#generated-files>)
- [Dependencies](<#dependencies>)
  - [bzip2 backend](<#bzip2-backend>)
- [Linked worktrees](<#linked-worktrees>)
- [Project map](<#project-map>)

<!-- END doctoc -->

## Prerequisites

Install these system tools before the first build:

- [mise](<https://mise.jdx.dev/installing-mise.html>), which installs and pins everything in [`.config/mise.toml`](<https://github.com/rom-weaver/rom-weaver/blob/main/.config/mise.toml>) - including Rust and Node.js, so neither is a prerequisite here
- CMake, Clang, and a native compiler toolchain
- [WASI SDK](<https://github.com/WebAssembly/wasi-sdk/releases>) v33 or newer for web builds
- Brotli for optimized production WASM builds
- ccache (optional) to reuse the compiled C objects across target directories and worktrees
- sccache (optional) to speed up repeated Rust builds

On macOS with Homebrew:

```bash
brew install mise cmake llvm brotli ccache sccache
```

WASI SDK is detected in `/opt/wasi-sdk`, `/opt/homebrew/opt/wasi-sdk`, or `~/.local/toolchains/wasi-sdk-<version>`. Set `WASI_SDK_PATH` when it is installed elsewhere. That detection is [`scripts/wasm/detect-wasi-sdk.sh`](<https://github.com/rom-weaver/rom-weaver/blob/main/scripts/wasm/detect-wasi-sdk.sh>), and it is POSIX shell rather than Node.js because mise [renders `[env]`](<https://mise.jdx.dev/templates.html>) before installing the tools it pins.

[mise-env]: https://mise.jdx.dev/templates.html

## Clone and bootstrap

All vendored source is committed in-tree, so a plain clone is complete:

```bash
git clone https://github.com/rom-weaver/rom-weaver.git
cd rom-weaver
```

Trust and install the pinned toolchains, install both JavaScript dependency sets, and install the repository hooks:

```bash
mise trust
mise install
npm ci
npm ci --prefix packages/rom-weaver-webapp
npm --prefix packages/rom-weaver-webapp exec playwright -- install chromium
npm run hooks:install
```

Run `mise tasks` at any time to list the supported task entry points.

## Build and run the webapp

Choose the WASM build that matches the task:

```bash
mise run wasm-check        # type-check the threaded WASI target
mise run build-wasm        # fast development build
mise run build-wasm-prod   # optimized release build with wasm-opt and Brotli
```

Choose an unused port for each worktree, then start the HTTPS development server. This example uses port 5174; use another port if it is occupied:

```bash
export PORT=5174
npm run dev
```

Open the URL that the server prints. The local certificate may need a browser exception or trust setup. The server supplies HTTPS and the COOP/COEP headers needed for the production `SharedArrayBuffer` runtime.

`npm run dev` rebuilds WASM when the artifact is missing or its recorded source fingerprint differs from the current inputs. Run `mise run build-wasm` directly after changing the installed WASM toolchain or when you need the complete build output.

By default, build artifacts are written to `packages/rom-weaver-webapp/src/wasm/`. The binary and attribution outputs are gitignored; runtime source and generated TypeScript remain tracked. To keep a separate artifact directory:

```bash
ROM_WEAVER_WASM_OUT_DIR=/path/to/artifacts mise run build-wasm
```

The build synchronizes the artifacts back into the webapp package when the custom directory differs from the default. The threaded target flags and WASI compiler wiring live in `.cargo/config.toml` and `.config/mise.toml`; use `mise exec` for ad hoc target commands:

```bash
mise exec -- cargo check -p rom-weaver-containers --target wasm32-wasip1-threads
```

To synchronize an existing artifact directory without rebuilding, run this command from `packages/rom-weaver-webapp`:

```bash
npm run prepare:dist -- /path/to/wasm-artifacts
```

See the [WASM runtime notes](<https://github.com/rom-weaver/rom-weaver/blob/main/packages/rom-weaver-webapp/src/wasm/README.md>) for the browser OPFS and worker API.

## Build and run the native CLI

```bash
cargo build -p rom-weaver-cli
cargo run -p rom-weaver-cli --bin rom-weaver -- --help
```

The `rom-weaver-cli` package contains the reusable `rom_weaver_app` command library plus the native CLI, WASM entrypoint, and type generator.

## Test and lint

Run the complete local quality gate before submitting a change:

```bash
mise run ci
```

That task covers Rust formatting, Clippy, generated types, threaded-WASM guards, license inventory, workflow/shell/Dockerfile linting, Rust tests, the production WASM build, frontend linting, unit tests, browser/WASM tests, full-browser tests, webapp E2E, and the production frontend build.

The frontend `lint` command is the source of truth for formatting, Oxlint and Biome (including complexity limits), TypeScript, browser compatibility, unused exports, copy/paste duplication, i18n catalog integrity, and the legacy-string audit. The pre-commit hook selects these and the Rust checks from the staged paths; CI runs its lint jobs over the whole tree.

Useful narrower checks:

```bash
mise run fmt
mise run clippy
mise run test-rust
mise run typegen-check
mise run actionlint    # GitHub Actions workflows and composite actions
mise run shellcheck    # tracked shell scripts
npm test               # test the Node.js tooling
mise run hadolint      # Dockerfiles

npm --prefix packages/rom-weaver-webapp run lint    # complete frontend lint fan-out
npm --prefix packages/rom-weaver-webapp run test:unit
npm --prefix packages/rom-weaver-webapp run test:browser
```

Use the repository's browser-test runner instead of invoking browser Vitest directly; it isolates files and avoids browser-mode hangs in linked worktrees.

The fast end-to-end gate is also available separately:

```bash
mise run test-e2e-fast
```

The [Mobile Safari guide](<https://github.com/rom-weaver/rom-weaver/blob/main/docs/development/mobile-safari-verification.md>) owns the WebKit, nightly matrix, iOS Simulator, and real-device verification procedures.

## Generated files

When Rust command types or metadata change, regenerate the TypeScript surface:

```bash
mise run typegen
mise run typegen-check
```

The build generates third-party attribution files from the resolved Cargo dependency graph. Public-domain and no-attribution-only licenses are omitted. To check that generation still works:

```bash
mise run licenses-check
```

When Clap commands or argument help changes, regenerate the man pages from those definitions (they are generated artifacts, not checked in):

```bash
mise run manpages
man ./docs/man/rom-weaver.1
```

The webapp's sample NES ROMs, IPS patches, and ZIP weaves are reproducible from source. Generate the four public sample files in `packages/rom-weaver-webapp/dist/` with:

```bash
npm --prefix packages/rom-weaver-webapp run samples:generate
```

Do not edit files under `packages/rom-weaver-webapp/src/wasm/generated/` or `docs/man/` manually.

## Dependencies

`.config/deny.toml` owns the dependency policy - advisories, licenses, and sources. Check it with:

```bash
mise run deny             # advisories + licenses + sources
mise run deny-policy      # licenses + sources only (the gating half)
mise run deny-advisories  # advisories only
mise run machete          # unused Rust dependencies
```

The two halves are split because CI treats them differently: licenses and sources gate the build, while advisories run in the deliberately non-gating [`security` job](<https://github.com/rom-weaver/rom-weaver/blob/main/docs/development/ci.md#jobs>) alongside `npm audit`. Findings show up as job annotations and a run summary, and are still expected to get fixed.

New crates must resolve to an already-allowed license. When one does not, prefer a per-crate entry in `exceptions` over widening the global `allow` list, and write down why - a scoped entry keeps the next crate carrying that license from landing unnoticed.

### bzip2 backend

`bzip2` exposes two backends and picks one with `#[cfg(feature = "bzip2-sys")]`: the C `bzip2-sys` library, or the pure-Rust `libbz2-rs-sys`. The workspace pins `bzip2` to `default-features = false, features = ["bzip2-sys"]`, which keeps every build on the C backend.

The `bzip2-sys` feature selects the C backend. Cargo combines features across the dependency graph, and `qbsdiff` enables bzip2's default features. Removing the workspace's `bzip2-sys` feature therefore selects the Rust backend and can change BDF patch output.

As a result, `libbz2-rs-sys` compiles but is never linked. Keep the scoped `.config/deny.toml` exception for its `bzip2-1.0.6` license. If `qbsdiff` later exposes a feature that disables its default bzip2 backend, remove the extra backend and the exception together.

## Linked worktrees

After creating a linked worktree, run its setup helper before building or testing:

```bash
node scripts/setup-worktree.mjs
```

The helper installs package dependencies, copies available WASM artifacts, and primes the pre-commit Cargo directories `target/hook-typegen` and `target/hook-wasm`. Pass `--no-prime` to leave those builds to the first Rust commit. Each worktree must use its own Cargo target directories; sharing them between checkouts can reuse stale staged C sources.

After verifying that a worktree has no real changes, remove it from the main checkout with:

```bash
node scripts/remove-worktree.mjs .worktrees/<name>
```

## Project map

- `crates/` contains the Rust format libraries, shared app orchestration, and native CLI.
- `packages/rom-weaver-webapp/` contains the React app, browser workers, OPFS adapters, PWA shell, and WASM package surface.
- `scripts/` contains build, test, release, and license automation.
- `docs/` contains the tutorials, how-to guides, reference, and explanation, plus hosting and development guides.

Read [ARCHITECTURE.md](<https://rom-weaver.com/docs/architecture.md>) for the full crate graph, ownership boundaries, worker model, generated-type path, and browser runtime flow.
