Utility: result - the previous command's result (Ok / Err)
Status: Built + QEMU-verified (osdev test files). The first slice of the shell's
command-result model. Trails CLAUDE.md; does not amend it.
1. What it is
Every shell command now produces a Result - Rust's, not a POSIX exit code. Success is
Ok; failure is Err(ShellError). result prints the previous command's result:
gsh> read /notes.txt
…contents…
gsh> result
Ok
gsh> read /nope
read: not found: /nope
gsh> result
Err(FileNotFound)
It is deliberately not a number and not an "exit code". Nothing exits between commands -
the shell stays alive and result asks "how did the last one go?" (exit/code are the POSIX
reflexes we drop, same family as exec→run; 0_conventions.md §8.) The value reads exactly
like a Rust Result: Ok, or Err(<Variant>).
2. The model - Ok is the check
execute() returns Result<(), ShellError>. The common path is just "is it Ok?" - a
caller (a future run aggregating pass/fail, or assert) never needs to know the variant names.
The variants exist for when you do want to pin a specific failure (negative tests):
Okis the check.is_ok()answers "did it work?" for 90% of uses.- Variants are opt-in.
Err(FileNotFound)names the category when you care; the human-readable detail (the path, etc.) was already printed by the command itself. Unknownis the catch-all. A failure not yet given its own variant isErr(Unknown), so every failure is at least that. Variants are added one at a time as commands are converted.
ShellError variants are unit (no payload): no_std/no-heap means a variant can't own a
String, and the detail belongs in the printed message anyway. The enum is the category; the
message is the detail.
3. Behaviour
resultreports the immediately preceding command. It is itself a command that succeeds, so runningresulttwice shows the real result, thenOk(the firstresult's success).- A blank line is not a command - it leaves the last result unchanged.
- A number is never involved. (If an external host tool ever needed one, it would be derived
from the variant at the very edge - like
date epoch- never the source of truth. Not built.)
4. Conversion status (incremental)
Every command is on the Result model. execute() returns the command's genuine Result -
there is no Ok-wrapping that could hide a failure:
- File/storage -
read,dir,cd,write(+append),mkdir,copy(+recursive),move,rename,delete,find,tree, and the filter built-ins' direct formmatch/count/sort/first/last:Okon success,Err(FileNotFound)for a missing path,Err(Unknown)otherwise. - Service-control -
spawn/kill/restart:Err(Denied)for a protected core / session-critical service (soassert fails spawn supervisorholds),Err(Unknown)otherwise. - Introspection -
caps <service>→Err(FileNotFound)for a name that isn't live;observe/observe now→Err(Unknown)if the view fails to spawn. drives- list /flash/label/reset→Erron no disk, a bad selector, an unknown subcommand, or a declined[y/N].- Pipelines -
pipe_runreturns the pipeline'sResult(a| assertsink sets it; a stage error isErr). - An unknown command is
Err(soassert fails typoholds; a typo in a script counts). - The info commands (
echo/about/mem/cores/date/status/clear/help) genuinely can't fail, so they returnOk- uniformly on the model, not specially wrapped. (A reboot that happens never returns;reboot cancel,reboot 10mand the other deadline forms returnOkorErrlike any command.) The exception isdate sync, which returnsErr(Unknown)for every way the sync did not happen (no time from the network,the clock service did not answer, and the others).
ShellError variants in use: FileNotFound, Denied, AssertFailed, Unknown. New ones are
added as a failure earns its own name (StorageUnavailable, EndpointDead, … reusing kernel
names where they surface - §7.7).
5. Later (separate so it can grow)
- Converting the commands is done (§4); what remains is giving more failures their own
variant instead of
Unknown. run <script>- built (utilities/33_run.md): executes a file of commands, aggregatingresultinto "ran N, failed M".assert- built (utilities/34_assert.md):assert ok/fails <cmd>(result) and… | assert contains X(content), so a script self-verifies the guardrails and the output.
6. Implementation shape & conformance
execute() threads the previous result as local session state (no global - services hold no
global mutable state, §3.9) and returns the new one; the main loop stores it. result is a shell
built-in. Conforms to 0_conventions.md: its own result help / result version via the shared
help_block, listed under Console in the top-level help.