Skip to content

Run and Inspect Finite Process Work

By the end of this chapter, you will have submitted copied document bytes to a local process, edited and saved while it waited, inspected its escaped output, reloaded its command and cancelled another run. Use the terminal prerequisites. The exercise uses trusted Ruby and /usr/bin/ruby on Linux.

Prepare an Isolated Check

From the repository root, build the terminal and create a temporary directory:

cargo build --locked -p rur-editor --bin rur-terminal-proof
process_editor="$PWD/target/debug/rur-terminal-proof"
process_demo=$(mktemp -d)
printf 'seed\n' > "$process_demo/note.txt"

Save this worker as $process_demo/finite-worker.rb. It records the copied input, waits for an explicit file, and emits that input plus hostile stderr bytes. The fail mode writes a separate file before exiting with code 7.

mode = ARGV.fetch(0)
STDOUT.binmode
STDERR.binmode
input = STDIN.read
File.binwrite('captured.bin', input)
File.binwrite('worker.pid', Process.pid.to_s)

if mode == 'fail'
  File.binwrite('effect.txt', 'changed before failure')
  exit 7
end

sleep 0.01 until File.exist?('continue')
STDOUT.write(input)
STDERR.write([27, 0, 255].pack('C*'))

Save this configuration as $process_demo/process.rb:

directory = Dir.pwd
worker = File.join(directory, 'finite-worker.rb')
Rur.configuration.command('finite-check', ->(context) {
  mode = context.argument
  context.process({
    'label' => 'Finite check',
    'executable' => mode == 'missing' ? '/no-such-rur-executable' : '/usr/bin/ruby',
    'argv' => ['--disable-gems', worker, mode],
    'cwd' => directory,
    'stdin' => context.observe.fetch('text').b,
    'env' => {'RUBYOPT' => '', 'RUBYLIB' => ''},
    'timeout_ms' => 30000
  })
}, {'kind' => 'compound', 'requires' => 'buffer', 'argument_bytes' => 32,
    'effects' => ['process']})

The process effect permits one copied request in a compound command. It does not change editor text. Arguments stay separate; no shell expands their bytes. cwd is explicit, and the process inherits the environment with the listed overrides. Configuration and children run with your permissions. A failed callback starts no child. Execution begins after successful command publication; a later spawn failure preserves any already committed editor changes.

Start inside the temporary directory so the worker's files stay there:

(cd "$process_demo" && env -u RUBYOPT -u RUBYLIB -u GEM_HOME -u GEM_PATH "$process_editor" --config process.rb --profile nonmodal note.txt)

Edit While the Check Waits

Alt-x opens command entry in both profiles. Invoke finite-check, type copy at its argument prompt and press Enter. In another shell, inspect $process_demo/captured.bin; it contains exactly seed and one newline.

Invoke show-progress to read the live operation. Ctrl-G closes this page without stopping the process. Invoke inspect-job and confirm its empty argument to inspect the most recent process in a separate ordinary buffer. It reports pending execution and output completeness. Invoke kill-buffer to close that details buffer and return to the note.

Type ! at the beginning of the note. Save with Ctrl-X Ctrl-S and wait for saved durably. The saved file now contains exactly !seed and a newline; the process still owns its original copied input.

While it runs, change the configuration label from Finite check to Reloaded check in another editor and invoke reload-configuration. The accepted operation continues with its historical label and input. Current native inspection and cancellation remain available; old callback contexts and old checked actions cannot acquire authority from the new configuration.

Release the waiting worker from the shell:

touch "$process_demo/continue"

Invoke inspect-job with an empty argument again. It reports exit code 0, complete streams and External effects: unverified. Its stdout contains seed followed by a displayed line break. Inspect stderr by entering the displayed process token followed by stderr 0, for example OWNER:REQUEST:GENERATION stderr 0. Copy the actual token from your result. The stderr page contains \x1b\x00\xff; those bytes are displayed as text and cannot clear the terminal or execute a command. When a page has more output, its Continue: line gives the next argument. Close each details buffer with kill-buffer.

Cancel and Read the Actual Outcome

Remove the release file from the other shell:

rm "$process_demo/continue"

Invoke finite-check with hold, then open show-progress. Select its row and press Enter. The detail page lists Action 1: cancel-progress. Close the page with Ctrl-G, then invoke feedback-action through Alt-x to request that checked action. It addresses the current operation revision and native identity. A stale action, a stopping job or an already finished job refuses cancellation. You can also invoke cancel-progress with a current native operation token supplied by inspection; the process-result token and a PID are not cancellation authority.

After reconciliation, inspect-job reports cancellation after start, actual exit/signal facts and cleanup. Cancellation cannot prove rollback of external effects. Dismissing feedback, clearing its history or closing a details buffer only changes presentation. The ordinary global Ctrl-G recovery command also has its established pending-job cancellation behavior; close the progress page before treating Ctrl-G as a global command.

Invoke finite-check with fail. Its exit code is 7 and effect.txt still contains changed before failure. Invoke it with missing to see a spawn-stage failure with Started: false and External effects: not-started. The editor remains usable in both cases. Exit zero established only execution success; none of these outcomes proves a domain result or undoes an external write.

Return to the note, type ? after !, save and exit with Ctrl-X Ctrl-C. Verify the exact bytes, including the newline, after the terminal restores its modes:

printf '!?seed\n' | cmp - "$process_demo/note.txt"

Repeat with a fresh directory and --profile modal. Before typing !, press i to enter Insert. Named commands and inspection work through Alt-x in both profiles. After returning from a details buffer, enter Insert again if the source pane is in Normal.

Recognize Finite Limits

Four outstanding jobs share admission across snapshot and process executors. One process worker supervises one process group at a time. A request admits at most one MiB of stdin, 32 arguments totalling eight KiB, 16 environment overrides totalling eight KiB, and a deadline of at most 30 seconds including queue time. The executable and working directory each admit at most 4096 bytes. Each stream retains at most 64 KiB; exceeding a limit stops execution and preserves its bounded prefix and honest completeness facts.

The result store keeps four memberships. Old copied details remain ordinary text, while evicted result tokens stop resolving. A full buffer registry can refuse a new details buffer without losing native execution or retained output. Trusted Ruby can inspect copied Rur.job_results and bounded Rur.job_output(token, 'stdout', offset, count) pages; a page is at most 16 KiB and remains subject to aggregate admission.

See the runtime request and result reference and the native job ownership reference for exact fields, limits and lifecycle rules. Children are trusted local programs; the editor's accounting is not a child-memory limit or a sandbox.

Previous: Reload Your Personal Configuration | Part | User Guide | Next: Modal Interaction