Files
2026-07-10 15:02:09 +01:00

4.0 KiB
Raw Permalink Blame History

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 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 23 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