78 lines
4.0 KiB
Markdown
78 lines
4.0 KiB
Markdown
# Self-Rewriting
|
||
|
||
## Restart Mechanism
|
||
|
||
The `restart` tool has **two modes**, branched on the `desktop` cargo feature:
|
||
|
||
| Mode | Behaviour | Triggers |
|
||
|---|---|---|
|
||
| **Headless** (`cargo run`, `run.sh`, Docker) | `libc::_exit(-1)` → exit code `255`. `run.sh` detects `255` and re-executes `cargo run`, which recompiles changed source files and relaunches. The supervisor is the only thing that can rebuild the binary. | The agent edited `src/**/*.rs` or `Cargo.toml` and wants the new code loaded. |
|
||
| **Desktop** (`--features desktop`, Tauri bundle) | `AppHandle::cleanup_before_exit()` (Tauri-side teardown) → `Command::new(current_exe).spawn()` → `std::process::exit(0)`. The same read-only bundled binary is relaunched — **no rebuild** (there is no source tree to rebuild from). | The user/agent wants to apply `config.yml` / DB changes that are only read at startup. Self-modification of `src/**/*.rs` has no effect in a bundle. |
|
||
|
||
The headless exit code `255` (`-1`) deliberately uses `libc::_exit()` rather
|
||
than `std::process::exit()` to skip C `atexit` handlers (e.g. Metal GPU cleanup
|
||
in `whisper-rs`, which would crash with SIGABRT and produce `134` instead of
|
||
`255`).
|
||
|
||
See [desktop.md](desktop.md) for the desktop-bundle architecture and the
|
||
`desktop::app_handle()` OnceLock that lets the tool reach the `AppHandle`.
|
||
|
||
---
|
||
|
||
## run.sh Exit Codes
|
||
|
||
| Exit code | Meaning | run.sh action |
|
||
|---|---|---|
|
||
| `0` | Graceful shutdown — SIGINT (Ctrl+C) **or** SIGTERM, both trapped in `main.rs` | Stop loop, exit 0 |
|
||
| `255` | Restart requested (`exit(-1)`) | `cargo run` again (recompile) |
|
||
| `143` | SIGTERM with no handler (`128+15`) — no longer reachable; see note | Stop loop, propagate code |
|
||
| other | Unexpected error (e.g. `101` panic) | Stop loop, propagate code |
|
||
|
||
`main.rs` traps **both** SIGINT and SIGTERM (`wait_for_shutdown_signal`) and runs the graceful shutdown path, so an external `kill` now exits `0` and logs `signal=SIGTERM` instead of dying silently with code `143`. To force a restart, use the `restart` tool (exit `255`) — never `kill` the process.
|
||
|
||
> The `run.sh` supervisor and exit-code table above apply only to **headless
|
||
> mode**. In desktop mode there is no supervisor; the Tauri process manages its
|
||
> own lifecycle and exit code `0` simply terminates the app.
|
||
|
||
---
|
||
|
||
## Safe Self-Modification Workflow
|
||
|
||
1. **Read** the relevant source files with `read_file` before making any changes.
|
||
2. **Edit** source files (`edit_file`, `write_file`, etc.).
|
||
3. **Check**: `execute_cmd` with command `cargo check 2>&1`. Inspect output.
|
||
4. **Fix** any compiler errors. Repeat steps 2–3 until clean.
|
||
5. **Restart**: call the `restart` tool only after a clean `cargo check`. The app rebuilds and relaunches automatically.
|
||
|
||
Never skip the `cargo check` step. A broken build will crash the supervisor loop with a non-zero non-255 exit code, stopping the app entirely.
|
||
|
||
---
|
||
|
||
## Requires Restart vs Does Not
|
||
|
||
| Change | Restart required? |
|
||
|---|---|
|
||
| `src/**/*.rs` | **Yes** |
|
||
| `Cargo.toml` / `Cargo.lock` | **Yes** |
|
||
| `agents/*/AGENT.md` | No — read at request time |
|
||
| `agents/*/meta.json` | No — read at request time |
|
||
| `config.yml` | No — read at startup only; take effect on next restart |
|
||
| `data/memory/**` | No — read at request time |
|
||
| `docs/**` | No |
|
||
|
||
---
|
||
|
||
## Risk Points
|
||
|
||
- **Never call `restart` mid-approval flow.** If a `PendingWrite` is waiting for user input, calling `restart` drops the `oneshot` sender, which unblocks the handler with an `Err` — the approval is cancelled and the tool call is aborted. Wait for the approval to resolve first.
|
||
- **Always check build before restart.** A compilation failure with `cargo run` returns a non-255 exit code, causing `run.sh` to stop the loop rather than retry.
|
||
- **`execute_cmd` requires user approval.** The user must approve the shell command in the UI before it executes.
|
||
|
||
---
|
||
|
||
## When to Update This File
|
||
|
||
- The restart mechanism or exit codes change
|
||
- The safe-modification workflow gains or loses a step
|
||
- New file types are added that do/don't require a restart
|