> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Watch SDK

> Automatic file watching, hot reload, and rebuild workflows for RBS targets

# Watch SDK

The Watch SDK provides file watching and automatic rebuild functionality. Enable hot-reload development workflows where source file changes automatically trigger rebuilds and process restarts — no manual intervention needed.

## Quick start

### Watch and rebuild

Add the `--watch` flag to any build command:

```bash theme={null}
# Watch for changes and rebuild automatically
rbs build //src:app --watch

# Output:
# 🔄 Initial build...
# ✅ Build succeeded
# 👀 Watching for changes... (Ctrl+C to stop)
#    Extensions: [.py .js .ts]
#    Directories: [/workspace/src]
```

### Watch and run

Combine `--watch` with `rbs run` to automatically restart your application on changes:

```bash theme={null}
# Watch, rebuild, and restart the process
rbs run //src:server --watch

# Output:
# 🔄 Initial build...
# ✅ Build succeeded
# 🚀 Starting: ./server --port 8080
# ──────────────────────────────────────────────────
# Server listening on :8080
# 👀 Watching for changes... (Ctrl+C to stop)
```

When you save a file, the watcher detects the change, stops the running process, rebuilds, and restarts:

```
🔄 Changes detected:
   📝 src/handlers.py
   📝 src/routes.py

⏹️  Stopping process...
🔨 Rebuilding...
✅ Build succeeded in 1.2s
🚀 Starting: ./server --port 8080
──────────────────────────────────────────────────
Server listening on :8080
👀 Watching for changes... (Ctrl+C to stop)
```

***

## CLI flags

```bash theme={null}
rbs build //target --watch [flags]
rbs run //target --watch [flags]
```

| Flag                 | Description                                               | Default       |
| -------------------- | --------------------------------------------------------- | ------------- |
| `--watch`            | Enable watch mode.                                        | `false`       |
| `--debounce <ms>`    | Milliseconds to wait after last change before rebuilding. | `300`         |
| `--clear`            | Clear the screen on each rebuild.                         | `false`       |
| `--no-clear`         | Don't clear the screen on rebuild.                        | —             |
| `--extensions <ext>` | Override watched file extensions (comma-separated).       | Auto-detected |

***

## Configuring watch behavior in rules

Rules can specify their own watch behavior using the `ctx.watch` API. This lets rule authors define sensible defaults for each language or framework.

### `ctx.watch.config()`

Set watch configuration from inside a rule implementation:

```python theme={null}
def _my_server_impl(ctx):
    # ... build logic ...

    # Configure watch mode for this target
    ctx.watch.config(
        extensions = [".py", ".html", ".css"],
        ignore_patterns = ["__pycache__/", "*.pyc", ".rbs/"],
        extra_watch_dirs = ctx.attr.watch_dirs,
        debounce_ms = 300,
        clear_screen = True,
    )

    return DefaultInfo(executable = output)
```

### Configuration options

| Option             | Type           | Default              | Description                                                                            |
| ------------------ | -------------- | -------------------- | -------------------------------------------------------------------------------------- |
| `extensions`       | `list[string]` | `[]` (all files)     | File extensions to watch (e.g., `.py`, `.ts`).                                         |
| `ignore_patterns`  | `list[string]` | `[".rbs/", ".git/"]` | Glob patterns to ignore.                                                               |
| `extra_watch_dirs` | `list[string]` | `[]`                 | Additional directories to watch beyond the target's sources.                           |
| `debounce_ms`      | `int`          | `300`                | Wait time in ms after the last change before triggering a rebuild.                     |
| `clear_screen`     | `bool`         | `false`              | Clear terminal screen before each rebuild.                                             |
| `signal_reload`    | `bool`         | `false`              | Send `SIGHUP` instead of restarting the process (for servers that support hot reload). |

### `ctx.watch.on_reload()`

For servers that support hot reload without a full restart, define a custom reload command:

```python theme={null}
def _dev_server_impl(ctx):
    # ... build logic ...

    # Support hot reload via custom endpoint
    ctx.watch.on_reload(
        command = "curl -X POST localhost:8080/__reload",
    )
```

***

## Language examples

### Python

```python theme={null}
# BUILD.rbs
load("@rbs//python/rules.rbs", "py_binary")

py_binary(
    name = "app",
    srcs = ["main.py"],
    deps = [":lib"],
)
```

```bash theme={null}
rbs run //:app --watch
```

Watch behavior:

* Extensions: `.py`
* Ignores: `__pycache__/`, `*.pyc`
* Debounce: 300ms

### Node.js / TypeScript

```python theme={null}
# BUILD.rbs
load("@rbs//nodejs/rules.rbs", "nodejs_binary")

nodejs_binary(
    name = "dev_server",
    entry_point = "server.ts",
)
```

```bash theme={null}
rbs run //:dev_server --watch
```

Watch behavior:

* Extensions: `.js`, `.ts`, `.jsx`, `.tsx`, `.css`, `.html`
* Ignores: `node_modules/`, `dist/`
* Debounce: 200ms

### Java / Kotlin (Spring Boot)

```python theme={null}
# BUILD.rbs
load("@rbs//java/rules.rbs", "java_binary")

java_binary(
    name = "server",
    srcs = glob(["src/**/*.java"]),
    main_class = "com.example.Application",
    deps = [":spring_boot_web"],
)
```

```bash theme={null}
rbs run //:server --watch
```

Watch behavior:

* Extensions: `.java`, `.kt`, `.properties`, `.yaml`
* Ignores: `target/`, `build/`
* Debounce: 500ms

### Custom watch configuration

```python theme={null}
def _custom_dev_impl(ctx):
    output = ctx.actions.run(
        command = "make build",
        outputs = [ctx.actions.declare_file("app")],
    )

    ctx.watch.config(
        extensions = [".c", ".h", ".cpp", ".hpp"],
        ignore_patterns = ["build/", "*.o", "*.a"],
        extra_watch_dirs = ["include/", "lib/"],
        debounce_ms = 500,
        clear_screen = True,
    )

    return DefaultInfo(executable = output)

custom_binary = native.define_rule(
    implementation = _custom_dev_impl,
    attrs = {
        "srcs": attr.label_list(),
    },
)
```

***

## How debouncing works

The watcher uses debouncing to batch rapid file changes into a single rebuild. When multiple files are saved in quick succession (e.g., from an IDE "Save All"), only one rebuild is triggered:

```
File change: main.py      → Start 300ms timer
File change: utils.py     → Reset timer to 300ms
File change: handlers.py  → Reset timer to 300ms
... timer expires ...
→ Rebuild once with all 3 changes
```

Recommended debounce settings:

| Scenario                                 | Recommended debounce |
| ---------------------------------------- | -------------------- |
| Fast compile languages (Python, Node.js) | 200–300ms            |
| Medium compile (Java, Kotlin)            | 300–500ms            |
| Slow compile (C/C++, Rust)               | 500–1000ms           |
| IDE auto-save enabled                    | 500ms                |

***

## Process management

### Graceful shutdown

When files change and a process is running, the watcher:

1. Sends `SIGTERM` to the process group.
2. Waits up to 3 seconds for graceful shutdown.
3. Sends `SIGKILL` if the process is still running.
4. Runs the rebuild.
5. Starts the new process.

<Tip>Make sure your application handles `SIGTERM` gracefully — close database connections, flush buffers, and shut down cleanly.</Tip>

### Signal reload (hot reload)

For servers that support hot reload via `SIGHUP`, enable signal mode to avoid full restarts:

```python theme={null}
ctx.watch.config(
    signal_reload = True,
)
```

The watcher sends `SIGHUP` instead of killing and restarting the process. The server can reload configuration or recompile templates without downtime.

***

## Troubleshooting

### Infinite rebuild loop

**Symptom**: Build triggers, outputs trigger another build, repeat.

**Solution**: Add output directories to ignore patterns:

```python theme={null}
ctx.watch.config(
    ignore_patterns = ["build/", "dist/", ".rbs/"],
)
```

### Too many open files

**Symptom**: Error about file descriptor limit.

**Solution**: Reduce the watched directories or increase the file descriptor limit:

```python theme={null}
ctx.watch.config(
    extra_watch_dirs = ["src/"],  # Watch only source directories, not the entire workspace
)
```

### Process not stopping

**Symptom**: Old process keeps running after rebuild.

**Solution**: Ensure your application handles `SIGTERM` and exits cleanly. If your process spawns child processes, make sure they are in the same process group.
