Skip to content

realm run: commands that act

realm run has seventeen subcommands. This page covers the eleven that change a run or the store. The six that only read are in realm run: commands that read. Every output shown came from a run of the command.

Subcommand What it does Acts when
respond Answers a gate. Always
advance Runs what a run owes the engine, with no model. Always
resume Makes a failed step runnable again. Always
abandon Ends an open run. Always
cleanup Abandons every open run that has been idle for a given time. Unless --dry-run is given
reclaim Frees a step that was started and never finished. Only with --force
drain Runs the cleanup steps a finished run still owes. Only with --force
purge Deletes finished runs. Only with --force
gc Deletes files that a crash left in the store. Only with --force
reconcile Rebuilds the index of idempotency keys. Unless --dry-run is given
migrate Records the ending of runs that were written by old versions. Only with --force

A duration is a whole number followed by d, h or m: 30d, 6h, 10m. A run ID that is not in the store gets Run not found: <id> and exit code 1.

When the @sensigo/realm the workflow’s code imports is not the version the command runs, the command prints REALM_RELEASE_LINE_MISMATCH to stderr once per copy and goes on. This was added in 0.46.0.

realm run respond <run-id> --gate <gate-id> --choice <choice> [--by <name>] [--project <dir>] [--extensions-module <path>]

Answers the gate a run is waiting at. realm run inspect <run-id> prints the gate’s ID and its choices.

Flag Required What it does
--gate <gate-id> Yes The ID of the open gate.
--choice <choice> Yes One of the gate’s choices.
--by <name> No Who made the choice. Recorded with the answer as given, and not checked. At most 200 characters, no control characters. Added in 0.46.0.
--project <dir> No Used only for a workflow registered without a project folder (made by an agent or from a string): the folder whose realm.yaml and code it loads. Default: the current folder. A workflow registered from a folder loads its code from there, and --project is not used (the command says so).
--extensions-module <path> No Loads this code file in place of the files named by the workflow’s extensions.
Terminal window
realm run respond 3ebc1158-1d29-41b5-9ca5-df054a681b58 --gate 0d499c26-a6b4-406f-ae29-6d6ef5c75fcb --choice approve
Responded: 3ebc1158-1d29-41b5-9ca5-df054a681b58 | choice 'approve' | new state 'running'

new state is the run’s phase after the answer: running if steps remain, completed if the gate’s step was the last.

When the answer leaves auto steps that only the engine can run, one more line names them and the command that runs them (runs it for one step, runs them until a step opens a question, fails or ends the run for more: see advance), where that command loads the steps’ project code from (the workflow’s own project folder, whatever folder the shell is in; for a workflow made without one, the folder it runs in or its --project), and that the environment is that shell’s. Added after version 0.46.0:

Responded: b178179a-998d-457e-85e6-6d38439d0585 | choice 'approve' | new state 'running'
Owed to the engine: 'process', 'notify' — realm run advance b178179a-998d-457e-85e6-6d38439d0585 runs them until a step opens a question, fails or ends the run, with the project code under /home/me/project, in the environment of the shell it runs in.
If a realm workflow run or realm agent is still waiting on this run, it goes on by itself; the line above is for when none is.

When the workflow’s own project folder holds no project code (no realm.yaml, no extension module), the line says with no project code (nothing to load under <folder>) instead. When --project is given for a workflow that has its own project folder, the first line says it was not used: --project <dir> was not used: workflow '<id>' has its own project, <folder>, and its code is loaded from there., or, when that folder holds no project code, … has its own project, <folder> (no project code there). Both added after version 0.46.0. With --extensions-module, that folder’s extension modules are not loaded, and the line ends …, and its realm.yaml is loaded from there., or … <folder> (no realm.yaml there). when it has none.

When the answer leaves an agent step ready, the line says so in the words realm run advance uses, with the command that drives it. Added after version 0.46.0:

Responded: 4168cf62-7ae2-4ed9-a688-e149ccbe06a6 | choice 'approve' | new state 'running'
An agent step is ready: 'finish' — drive it with realm agent --run-id 4168cf62-7ae2-4ed9-a688-e149ccbe06a6 --provider <provider> --model <model>
If a realm workflow run or realm agent is still waiting on this run, it goes on by itself; the line above is for when none is.

After the lines that name a command — Owed to the engine: …, An agent step is ready: … — one more line says that a realm workflow run or realm agent still waiting on the run goes on by itself (the lines above are when there are two): the run’s record does not show whether one is waiting, so the line holds either way. Added after version 0.46.0.

When the answer leaves nothing that can run from here — no agent step ready, no owed step that can run, only auto steps that cannot run — each such step is named, then the way out. Added after version 0.46.0:

Responded: 77772f0c-a80d-49d3-b665-4ac186186fd1 | choice 'approve' | new state 'running'
'compute' cannot run (input_schema): Invalid input for step 'compute': the input must have required property 'n'.
Run 77772f0c-a80d-49d3-b665-4ac186186fd1 stays open (phase 'running'): for 'compute', the engine gives it no input, so correct its input_schema and register the workflow again; then, after a fix, realm run advance 77772f0c-a80d-49d3-b665-4ac186186fd1 — or end it: realm run abandon 77772f0c-a80d-49d3-b665-4ac186186fd1

A step that needs a handler or adapter this program lacks ends with its own way out (— load the missing extension, or run the step on a runner that has it.), and when no step is refused before its claim the last line is To end the run instead: realm run abandon <id>

When the answer leaves nothing to name but a step in flight in another program, the line says to wait for it, in the words realm run advance uses. Added after version 0.46.0:

Responded: 5c1e8a42-3f0b-4d7e-9a61-2b8f0c4d7e13 | choice 'approve' | new state 'running'
'pack' is in flight in another program — wait for it, or see realm run inspect 5c1e8a42-3f0b-4d7e-9a61-2b8f0c4d7e13

respond records the answer. Without --by the answer names nobody, and realm run inspect shows (not stated). With --by, the Responded: line names the answerer: Responded: <id> | choice '<choice>' | answered by <name> (as stated) | new state '<phase>'. A name is never taken from the operating system or from REALM_OPERATOR: those name a program, and respond uses REALM_OPERATOR only for the cleanup steps its answer runs. An empty, long or control-character name is refused before the run is read:

--by: empty; nothing was recorded. Give a name of at most 200 characters with no control characters, or leave it out.

A REALM_OPERATOR that cannot be used refuses the answer too, even when the workflow has no cleanup steps, because respond checks it before it reads the run:

REALM_OPERATOR: contains a control character; it is written as the program's name on any cleanup steps the answer lets run, and respond checks it before reading the run, so nothing was recorded. Unset it or give it a name of at most 200 characters with no control characters.

The middle word is empty, longer than 200 characters or contains a control character. respond never passes a claim_token; the answer’s proof reads no claim_token passed on inspect.

A guard step that the answer makes ready is decided in the same write, and what it did is printed before the Responded: line. No other step runs: the steps that follow run when a driver next calls the run.

A guard that passed, with steps still to run:

Guard step 'only_if_shipping' passed.
Responded: 989c0619-1bda-4b2e-b5e3-4d33d6519a67 | choice 'ship' | new state 'running'
An agent step is ready: 'ship' — drive it with realm agent --run-id 989c0619-1bda-4b2e-b5e3-4d33d6519a67 --provider <provider> --model <model>
If a realm workflow run or realm agent is still waiting on this run, it goes on by itself; the line above is for when none is.

A guard that ended the run. Reason: is the guard’s abort_message, and is left out when the guard has none:

Guard step 'only_if_shipping' aborted the run.
Reason: The order was held.
Responded: c267f37e-bddb-46aa-aea2-b14f62ba358e | choice 'hold' | new state 'aborted'

The first line is one of three sentences: Guard step '<step>' aborted the run., Guard step '<step>' failed with a resolution error. Run is terminated., or Guard step '<step>' passed and completed the run. A guard that failed is listed as a failed step realm run resume takes, so the second sentence goes on with the way back in: To make the failed step runnable again: realm run resume <id> --from <step> (added after version 0.46.0). When the run ended and it has cleanup steps, they run in this process, and one line follows for each cleanup step this ending ran or left pending: finalizer '<name>': <status>. A cleanup step that completed or failed at an earlier ending, before realm run resume, does not run again and gets no line. When one is left pending (this program has no handler for it), the next line names the command that runs it: Cleanup step left pending: '<name>' — to run it with code that has its handler: realm run drain <id> --force (added after version 0.46.0).

Giving the same answer again prints the Responded: line again, naming the answerer the record holds (answered by <name> (as stated), or answered by (not stated) when you gave --by and the record holds no name); it records no new answer.

Without --by, a repeat of an answer that names nobody prints no answered by part, as the first answer did. A recorded name that cannot be printed, which only a record written outside these commands can hold, is shown as answered by a recorded name that cannot be printed (control characters, or not a name with its source), with or without --by. A repeat of the recorded answer is printed the same way after the run has ended, with the run’s phase (for example new state 'abandoned'), and exits 0. The refusal “The run has ended” below is for an answer to a question that was still open when the run ended, for example one whose time ran out with on_expiry: abort.

An answer that arrives after the gate’s time is up is not recorded, and for a gate that was settled with its default choice Not recorded: takes the place of Responded:. The lines after it are the ones above: what the run owes (added after version 0.46.0). When this answer is the call that carried out the expiry, its first line says so, as realm run advance’s does (where the expiry’s choice made a guard ready, respond prints the guard’s line on its own, after the first line; realm run advance adds the guard’s sentence to the end of its first line): ⚠ gate '<gate>' on '<step>' had expired <how long> before this call — this respond call first carried out its declared settle_default: the default choice '<choice>' was recorded (enacted_via: respond)., or … its declared abort: the run ended (enacted_via: respond). (added after version 0.46.0). See An answer after the time is up.

Exit code: 0 if the call succeeded, otherwise 1. An answer that was recorded exits 0, also when the guard it made ready aborted the run. A late answer that names the choice the gate was settled with exits 0, although it was not recorded. A refused answer exits 1. This differs from realm workflow run, which exits 1 for a run that ended as aborted.

The refusals:

Case Message
--gate is not the open gate Gate 'wrong' is not the open gate and matches no committed resolution.
--choice is not one of the choices Choice 'maybe' is not valid. Expected one of: approve, reject
The gate was answered differently Gate '70d76b3b-…' was already resolved with choice 'approve' — your choice 'reject' was not recorded.
The gate’s time was up, and it was settled with another choice Gate '80e024ee-…' was settled by timeout with choice 'hold' — your choice 'ship' was not recorded., after the ⚠ line when this answer carried out the expiry. Then what the guard did, if this answer carried out the expiry — a guard that failed goes on with the way back in, To make the failed step runnable again: realm run resume <id> --from <step> (added after version 0.46.0) — the Not recorded: line, and what the run owes.
The run has ended Run '00b33778-…' is terminal (aborted); cannot submit a gate response — an aborted run is never resumed; 'realm run purge 00b33778-…' previews what it would remove. The words after the dash name the way out that kind of ending has, as submit_human_response names it: see A run that has ended.
realm run advance <run-id> [--project <dir>] [--extensions-module <path>]

Added after version 0.46.0. Runs what a run owes the engine, from this shell — no model provider, no key: first, when the open question’s time is up and it declares on_expiry, that default or abort (the preview names it as the expired question on '<step>' (its declared <on_expiry>), and for a declared default goes on ; then it runs what that leaves owed until a step opens a question, fails or ends the run., and the command prints the line from its reply before the steps it runs: ⚠ gate '<gate>' on '<step>' had expired <how long> before this call — this advance call first carried out its declared settle_default: the default choice '<choice>' was recorded (enacted_via: advance)., or … its declared abort: the run ended …; when the default’s choice made a guard ready, the guard’s sentence follows on the same line: … (enacted_via: advance). Guard step '<guard>' passed.); then the guards and auto steps that are ready. It runs them one at a time, and the steps the run owes after them, and stops at the first step that opens a question, fails or ends the run: a step named after that one does not run in that call. When the preview names more than one, its line says so: Owed to the engine: 'approve', 'fetch'; it runs them until a step opens a question, fails or ends the run. It loads the project’s extensions exactly as respond does (--project, --extensions-module) — from the workflow’s own project folder, whatever folder the shell is in, or, for a workflow made without one, from --project or the folder it runs in — names this program with REALM_OPERATOR or the OS user (a REALM_OPERATOR that cannot be used prints one line and exits 1 before any work), and prints what it is about to do before it runs anything:

Advancing run <id> (workflow 'cli-owed-wf') with no project code (nothing to load under /home/me/project), in this shell's environment.
This program: tester (from REALM_OPERATOR) · project code: neither side records project code.
Last recorded driver: none recorded.
Owed to the engine: 'after'.
→ after
Stopped: an agent step is ready: 'finish' — drive it with realm agent --run-id <id> --provider <provider> --model <model>
If a realm workflow run or realm agent is still waiting on this run, it goes on by itself; the line above is for when none is.
Run <id>: phase 'running'

Each guard it decides prints Guard step '<guard>' passed., also when a later step completes the run, in the order the steps ran: a guard decided before a later step is printed before that step’s line. The guard that ended the run prints its sentence (Guard step '<guard>' aborted the run.) and Reason: when it has one. A guard that failed — its path found nothing — is a step that failed: a Stopped: '<guard>' failed: <reason> line follows, and the exit code is 1, as for an auto step that failed.

The first line names the folder the project code is loaded from, or says with no project code (nothing to load under <folder>) when that folder holds none (no realm.yaml, no extension module); a --project the workflow’s own project folder overrides is said on the next line, --project <dir> was not used: workflow '<id>' has its own project, <folder>, and its code is loaded from there., or … <folder> (no project code there). when it holds none (both added after version 0.46.0). With --extensions-module, the first line names that module, with the module <path> (--extensions-module), followed by and the realm.yaml of <folder> only when that folder has a realm.yaml, which the command then reads; the folder’s extension modules are not loaded, so the --project line ends …, and its realm.yaml is loaded from there., or … <folder> (no realm.yaml there). when it has none. --project is used only for a workflow registered without a project folder (realm run advance --help says so). The expiry line is printed when the call carries the expiry out, before the steps it led to; every other line in the reply’s warnings is printed as ⚠ <line> after the steps that ran (added after version 0.46.0, which printed only the expiry line, on stderr, after the steps). At an open question the command runs nothing, and its Nothing is owed to the engine: a question is open — realm run respond … line is composed from the run’s record as the command read it: when another program answers the question meanwhile, the steps that answer made ready are left for the next realm run advance. That command, here and in the Stopped: line of a question the call opened, gives the choices as --choice <one of: approve, reject>, a placeholder to replace, or the choice itself when the question has only one.

The preview’s project code words compare the code this program loaded with what the run last recorded:

Words Means
same as the run's last record The same files with the same hashes.
differs from the run's last record Comparable, and different: a code file, an entry module, the realm.yaml or the --extensions-module override changed.
not comparable with the run's last record The run records project code and this program loaded none, the two fingerprints were taken under different rules, one was cut short at its size limit, or one side’s load failed.
not comparable — the run has recorded no project code yet This program loaded project code, and the run has recorded none: no program with project code has run a step of it yet, as on a run just created. It is not a mismatch.
neither side records project code Neither the run nor this program loaded project code.

Last recorded driver: names the program on the run’s newest evidence entry that names one, with that entry’s step and time: <program> at step '<step>', <time>., or none recorded. A step’s own entry names the program that ran it when that program recorded its name; an answer to a question names none, and neither does a guard’s decision. So the program that answered a question is not named here: after an answer whose write ran no cleanup step, the line still names the program that ran the question’s step and opened the question. realm run inspect shows who answered, on that step’s Answer: line (answered by <name>, the name --by gave, or answered by (not stated)). When newer entries name no program, the line ends ; 1 newer entry records no driver. (or ; <n> newer entries record no driver.). Each one counts but a question’s answer, the entry its expiry writes and a guard’s decision: a step run by a program that recorded no name (a library call made without driver, for one), and an entry realm writes without running a step, such as the one realm run reclaim writes for the step whose claim it frees.

When another program holds an owed step, the preview says so before anything runs, once, with the program and the time: In flight: '<step>' is in flight, taken by <program> since <time>.

Stopped: lines say why it stopped, one line for each reason that holds, in this order: a step that failed ('<step>' failed: <error>, only when the run’s record lists the step as failed, and never for a step it started whose outcome was not recorded: when another program’s run of that step failed, the step’s own line says so, …, and failed; this program's outcome for it was not recorded. (below), and no failed line is printed), a refusal that failed no step ('<step>': <error>: the step it is about, with the engine’s words, or the engine’s words alone when the refusal names no step), the run ended (the run has ended (<phase>); when an engine failure ended it and a step failed that realm run resume takes, it goes on — to make '<step>' runnable again: realm run resume <id> --from <step>, or, for several, — to make a failed step runnable again: realm run resume <id> --from <one of: …> with their names; when an operator ended it, it goes on . An operator ended this run, with the reason "<reason>"; to run the work again, start a new run., never with realm run resume, which would erase that ending and its reason), a question opened (with the realm run respond command; when steps wait for its answer — ready by what they depend on, but held while a question is open — it names them first: a question is open ('<step>' waits for its answer) — realm run respond …, and the preview’s Nothing is owed to the engine: line does the same), each step that cannot run ('<step>' cannot run (<check>): <why>, or cannot run here (capability) for a handler or adapter this program lacks, ending with its way out: — load the missing extension, or run the step on a runner that has it), work the engine still owes when the call stops, after a refusal for one (the engine still owes '<step>' — to run it: realm run advance <id>, or, for several, the engine still owes '<a>', '<b>' — to run them until a step opens a question, fails or ends the run: realm run advance <id>), agent steps ready (an agent step is ready: '<step>' — drive it with realm agent --run-id <id> --provider <provider> --model <model> for one, agent steps are ready: '<a>', '<b>' — drive them with … for several; put the provider and model you drive the run with in place of the two placeholders; the next line is the one respond prints after its commands, If a realm workflow run or realm agent is still waiting on this run, it goes on by itself; the line above is for when none is., also after a preview line that names agent steps ready), each step another program holds ('<step>' is in flight in another program — wait for it, or see realm run inspect <id>), and otherwise nothing is ready to run now. A run the command completes gets no Stopped: line: the phase line says it. When the run ends during the call and has cleanup steps, one line per cleanup step that ending ran or left pending follows, just before the phase line, in the order the engine runs them, with the status the run’s record holds for it: finalizer '<name>': <status> — completed, failed when its handler threw, or pending when this program could not run it (a ⚠ line above says why). A cleanup step that completed or failed when the run ended before, and realm run resume reopened the run, does not run again and gets no line. When the ending left cleanup steps pending, the next line names them and the command that runs them: Cleanup step left pending: '<name>' — to run it with code that has its handler: realm run drain <id> --force (Cleanup steps left pending: '<a>', '<b>' — to run them with code that has their handlers: … for several); the preview of a run that ended with cleanup steps pending prints the same line after its last line. While another drainer’s lease on one of them has not passed, the line is Cleanup step left pending: '<name>' — held by another drainer's lease until <time> (realm cannot tell whether it is still running) — after <time>: realm run drain <id> --force, and a ⚠ finalizer '<name>' left pending — held by another drainer's lease until <time> (realm cannot tell whether it is still running) line above says why it is pending (added after version 0.46.0). A step another process took while this one was about to run it is said as a fact, and the command goes on with what is left. The command prints → <step> as it starts a step, before it claims it, so the losing program prints both lines, in this order — the second says another program took the step first, so it did not run here:

→ process
• Step 'process' was taken by racer-a (from REALM_OPERATOR, via advance) at 2026-10-05T00:02:25.302Z; not run here.

When another program settles the step or takes it over (after realm run reclaim <id> --step <step> --force freed this program’s claim, for one), or ends the run (realm run abandon, for one), while this one is running it, the outcome this program reached for the step is not recorded. The command says so, read from the run’s record, never that the step failed, and goes on with what is left:

→ process
• Step 'process' was taken by racer-b (from REALM_OPERATOR, via advance), and completed; this program's outcome for it was not recorded.
→ notify
Run 65d2afc8-2cb3-4401-808c-1d83a40bf989: phase 'completed'

The line ends , and failed; … when the other program’s run of the step failed. While the other program still holds the step it reads • Step '<step>' was taken by <program> at <time>; this program's outcome for it was not recorded., and when the run ended without the step settling, • Step '<step>': the run ended (<phase>) before this program's outcome for it was recorded. When no program holds the step and it has not settled, it reads • Step '<step>': another process removed the claim this program held on it; this program's outcome for it was not recorded., and the step is owed again: the command goes on with it as with any owed step. A step whose outcome was not recorded did not fail here, also when the other program’s run of it failed: the exit code is the one for what is left. When the record changed after the command read it and before it claimed the step (another program ended the run, or opened a question on another step), the step did not run here: no line says it did, and the Stopped: lines say what the record shows. Added after version 0.46.0.

When the engine can run nothing, the last preview line says why and nothing runs. It opens Nothing is owed to the engine: <reason> when nothing is owed (the run ended, a question is open, only agent steps are ready), and The engine can run nothing now: <reason> when steps are still owed to the engine but none can run here now (a step that cannot run, or a step in flight in another program). A reason that ends with a command ends the line there, with no full stop, so the command can be copied as printed; any other ends with a full stop. With more than one reason, the opening ends with its colon and each reason is a line of its own, indented two spaces (added after version 0.46.0, which joined them with ; and ended the line with a full stop). When the run stops on a step refused before its claim (an invalid trust, a failed precondition, an input its schema refuses) and nothing else is ready, the last line gives the way out — for a refused trust or precondition, correcting the workflow and registering it again is the fix, since the run picks up the corrected definition; for an input its schema refuses, the line names each such step’s own way (a new run with params that fit, or a corrected input_schema; with depends_on the engine gives the step no input, so a corrected input_schema; for a step that builds its input with an input_map, a corrected input_map or input_schema, or a new run with params that fit when the key its schema refuses is built from run.params):

The engine can run nothing now: 'compute' cannot run (input_schema): Invalid input for step 'compute': the input must have required property 'n'.
Run 507090b5-3b5b-4a6c-a814-3faa03404f95 stays open (phase 'running'): for 'compute', start a run with params that fit, or correct its input_schema and register the workflow again; then, after a fix, realm run advance 507090b5-3b5b-4a6c-a814-3faa03404f95 — or end it: realm run abandon 507090b5-3b5b-4a6c-a814-3faa03404f95

After a call that ran other steps, the same way out takes the place of the Run <id>: phase '<phase>' line. A step another program holds:

In flight: 'process' is in flight, taken by crown (from REALM_OPERATOR, via advance) since 2026-10-04T22:26:13.994Z.
The engine can run nothing now: 'process' is in flight in another program — wait for it, or see realm run inspect 65d2afc8-2cb3-4401-808c-1d83a40bf989

Exit code 1 when a Stopped: line gives a step that failed or a refusal, or when a step cannot run, else 0. The steps run in this shell’s environment (its secrets, its .env); two programs with the same code and different secrets look the same to the preview (#592).

realm run resume <run-id> --from <step> [--force]

Takes a failed step off the run’s list of failed steps and opens the run again. The run must be in failed or abandoned, and <step> must be listed under Failed by realm run inspect. Steps that completed stay completed.

Flag Required What it does
--from <step> Yes The failed step to make runnable again.
--force No Resumes even though another step of the run is marked in progress and Realm cannot tell how long it has been.
Terminal window
realm run resume 8d4053bf-ceb4-4bde-bbde-3595715fd7c5 --from fetch
Resumed run '8d4053bf-ceb4-4bde-bbde-3595715fd7c5': step 'fetch' re-enabled and run reset to 'running'.
Drive it with: realm agent --run-id 8d4053bf-ceb4-4bde-bbde-3595715fd7c5 --provider <provider> --model <model>
Add the other flags the run was driven with, such as --extensions-module or --project (realm run inspect 8d4053bf-ceb4-4bde-bbde-3595715fd7c5 shows the extension module the run loaded).

Fill in <provider> and <model> before you run the second line. <model> is the model to drive the run with. Give the flags you drove the run with: the model flags (--provider-module, or --provider, --model, --base-url and --strict-base-url) as you used them, and --extensions-module, --project, --schema-retries, --llm-timeout or --mint-writer-nonce if you used them. Realm does not record the model or most of those flags: realm run inspect shows the provider of a drive failure, and the extension module the run loaded under Extension Identity. A run started by realm workflow run was never driven by a model: name any provider and model you want. Version 0.45.0 prints the second line without --provider <provider> --model <model>, and no third line.

When the step that is ready again is one only the engine runs, one line names it (the step / the steps, and for more than one, until a step opens a question, fails or ends the run, before without a model) and the call that runs it without a model, and the Drive it with: lines are printed only when an agent step is ready too. Added after version 0.46.0, which prints the Drive it with: lines in this case too:

Resumed run '8bc06d55-77fb-43f4-937f-8d166fad20cf': step 'a' re-enabled and run reset to 'running'.
To run the step the engine owes ('a') without a model: realm run advance 8bc06d55-77fb-43f4-937f-8d166fad20cf

When the step that is ready again cannot run — the workflow was registered again with a check the step fails — and nothing else can run, driving the run would only stop on that step: the step and the way out take the place of the Drive it with: lines. Added after version 0.46.0, which prints the Drive it with: lines in this case too:

Resumed run '729eaab3-6203-46cb-9c5c-3714070723a8': step 'a' re-enabled and run reset to 'running'.
'a' cannot run (input_schema): Invalid input for step 'a': the input must have required property 'n'.
Run 729eaab3-6203-46cb-9c5c-3714070723a8 stays open (phase 'running'): for 'a', start a run with params that fit, or correct its input_schema and register the workflow again; then, after a fix, realm run advance 729eaab3-6203-46cb-9c5c-3714070723a8 — or end it: realm run abandon 729eaab3-6203-46cb-9c5c-3714070723a8

resume runs no step. Cleanup steps that had not yet run for the ended run are cancelled, and each is named on a line that starts with ⚠.

Exit code: 0 if the run was resumed, otherwise 1:

Case Message
The step did not fail Step 'review' is not in failed_steps for run '00b33778-…'.
The run is in another phase Run 4233aa3c-… is in phase 'completed', which is not resumable.
The step is a cleanup step Step 'release' is a finalizer — finalizers cannot be resumed via --from. Use 'realm run drain cba9901c-…' or '--void release' instead.
The run was aborted Run <id> was aborted (step '<step>') — aborted runs are never resumable.
A step is in progress, and within its time limit Step '<step>' has a HEALTHY claim (deadline <time>) — a live runner is presumed on it. Resume refuses to disturb live work.
A step is in progress, for an unknown time Step '<step>' has an unknown-age claim (no reliable deadline) — its liveness cannot be verified. Re-run with --force to override (only after confirming the runner is actually dead).
A cleanup step is being run now Run <id> has an active drain lease on finalizer '<name>' (expires <time>) — a drainer is executing NOW. …
realm run abandon <run-id> [--reason <text>]

Ends an open run. Its phase becomes abandoned. The workflow’s cleanup steps do not run.

Flag What it does
--reason <text> Recorded as the run’s cause. Default: Abandoned via realm run abandon.
Terminal window
realm run abandon 00b33778-f504-4d65-93ca-6300441f41e7 --reason "upstream still down"
Run '00b33778-f504-4d65-93ca-6300441f41e7' abandoned (phase: 'abandoned'). Reason: upstream still down.
To run the same work again: realm workflow run /srv/shop/sync --params '{"fail":true}' (the directory it was registered from) — a fresh run; this run's evidence stays at realm run inspect 00b33778-f504-4d65-93ca-6300441f41e7.
abandon is a kill — declared finalizers (if any) did NOT run and will not for this run. The graceful path is the workflow's own guard step (abort_unless), which runs them; there is no operator abort command.

The second line gives a command that starts a new run of the same workflow with the same parameters.

Abandoning a run that is already abandoned changes nothing, and the first line says so: … Already abandoned (no change this call). …

Exit code: 0 if the run is abandoned, otherwise 1:

Case Message
The run is waiting at a gate Run '4233aa3c-…' is waiting on human gate 'review' (gate '70d76b3b-…'); answer it before abandoning. Answer it: realm run respond 4233aa3c-… --gate 70d76b3b-… --choice <one of: approve, reject>.
The run ended in another way Run '08c61d5d-…' is already terminal (failed); cannot abandon a finished run.
realm run cleanup --older-than <duration> [--dry-run]

Abandons every open run that has not changed for <duration>. It leaves runs that are waiting at a gate.

Flag Required What it does
--older-than <duration> Yes How long a run must have been idle.
--dry-run No Lists the runs that would be abandoned and changes none.
Terminal window
realm run cleanup --older-than 7d --dry-run
Would mark 4 run(s) as abandoned.
• 0a8d87bf-b7fc-4983-96e2-3656718e231d
• 3ebc1158-1d29-41b5-9ca5-df054a681b58
• 6302fd4c-6a9f-4f17-b5ff-79574cb569b7
• cba9901c-fa22-47dd-97e2-47439238d01f
abandon is a kill — declared finalizers (if any) did NOT run and will not for this run. The graceful path is the workflow's own guard step (abort_unless), which runs them; there is no operator abort command.

Without --dry-run, the first line reads Marked 4 run(s) as abandoned. A run it abandons shows Sealed by: cleanup_sweep and Cause: Marked abandoned by realm cleanup in realm run inspect.

Exit code: 0, also when no run matches. 1 for a duration that cannot be read.

realm run reclaim <run-id>
realm run reclaim <run-id> --step <name> --force
realm run reclaim --all [--workflow <id>] [--older-than <duration>] [--force]

When a step starts, Realm marks it in progress and, where it can, records a time by which the step must have finished. If the process running the step dies, the mark stays and the run cannot move. reclaim reports such marks and, with --force, removes one so that the step can run again. The step’s work may then happen twice.

Flag What it does
--step <name> The step to free. Needs --force.
--force Frees the step, or with --all every selected step.
--all Selects, across all runs, every step that is past its time, is declared idempotent: true, and is not waiting at a gate.
--workflow <id> With --all: only runs of this workflow.
--older-than <duration> With --all: only steps that are past their time by at least this long.

With a run ID alone, it lists the steps in progress and the state of each:

Terminal window
realm run reclaim 70f9528e-4f45-4806-8690-c4e92a31760d
Run '70f9528e-4f45-4806-8690-c4e92a31760d' (running) — in-progress claims:
• fetch: claim_stale (deadline: 2026-10-01T23:15:36.957Z)
reclaim: realm run reclaim 70f9528e-4f45-4806-8690-c4e92a31760d --step fetch --force
Reclaim re-drives a step (at-least-once — side effects may repeat). Use --step <name> --force to act on a specific claim, or --all for batch auto-reclaim.
State Meaning
healthy The step’s time has not passed. Something is presumed to be running it.
claim_stale The step’s time has passed.
claim_unknown_age No time was recorded. This is so for agent steps, and for every step of a workflow that has cleanup steps.

The time is the step’s timeout_seconds across all its attempts plus one minute, and never less than 15 minutes after the step started.

A step that is waiting at a gate is listed with open gate — resolve via 'realm run respond <run-id>', not reclaim.

With --step and --force:

⚠ Reclaiming 'fetch' on run '70f9528e-4f45-4806-8690-c4e92a31760d' — this re-drives the step; its side effects may repeat.
Reclaimed 'fetch' (was claim_stale). It is eligible again — the next driver will re-drive it.

reclaim runs no step. The freed step runs when a driver next calls the run. The run’s record keeps an entry for the step that says it was reclaimed, with the state and the time it had.

Freeing a step that is still healthy is allowed, with a second warning first: ⚠ 'fetch' currently has a HEALTHY claim (a live runner is presumed on it). --force will override it and may double-drive live work.

With --all:

2 claim(s) WOULD be auto-reclaimed (idempotent ∧ past-deadline ∧ non-gated):
• 70f9528e-4f45-4806-8690-c4e92a31760d / fetch (deadline 2026-10-01T23:15:36.957Z, 0m past)
• 7252133d-1033-4300-8ddb-ff949f646a79 / fetch (deadline 2026-10-01T23:15:34.551Z, 0m past)
Re-run with --force to reclaim them. WARNING: each re-executes its handler at-least-once with NO per-step human judgment — enable only for genuinely idempotent handlers.

With --all --force:

⚠ Auto-reclaiming 1 claim(s); each re-executes its handler — side effects may repeat.
✓ 7252133d-1033-4300-8ddb-ff949f646a79: Reclaimed 'fetch' (was claim_stale). It is eligible again — the next driver will re-drive it.
Reclaimed 1/1 selected claim(s).

When nothing is selected, it prints No auto-reclaimable claims found (idempotent ∧ concrete-past-deadline ∧ non-gated). A run with no step in progress gets Run '<id>' (running) has no in-progress claims. Nothing to reclaim., and a run that has ended gets Run '<id>' is terminal (completed); it has no reclaimable claim.

Exit code: 0, also when there is nothing to free. 1 for a combination that cannot be used:

Provide a <run-id> to inspect/reclaim, or use --all for batch mode.
Refusing to reclaim 'fetch' without --force. Re-run with --force to re-drive it (the step re-executes; its side effects may repeat).
--force requires --step <name> (or use --all for batch). Run without arguments to see a dry-run.
--workflow and --older-than are only valid with --all.
Cannot combine a <run-id> with --all. Use one or the other.
--step is a per-run flag; it cannot be combined with --all.
realm run drain <run-id> [--force] [--expired] [--project <dir>] [--extensions-module <path>]
realm run drain <run-id> --void <finalizer>
realm run drain --all [--force] [--expired]

A workflow’s cleanup steps, the steps with execution: finalizer, run after the run has ended. If the process stops before they finish, the run stays ended and still owes them. drain reports the cleanup steps a run owes and, with --force, runs them.

Flag What it does
--force Runs the cleanup steps.
--all Every run that owes a cleanup step that can be run now.
--void <finalizer> Cancels one owed cleanup step without running it. Acts at once, without --force.
--expired Also carries out gates whose time has passed. See Gates.
--project <dir> The project whose realm.yaml applies if the workflow has no project of its own. Default: the current folder.
--extensions-module <path> Loads this code file in place of the files named by the workflow’s extensions.

Without --force, it reports:

Run '64e1c3a6-5882-4955-a796-a03355fe97f4' (completed) — pending finalizers, rank order:
• [0] release: actionable — would lease and run on --force, if its handler resolves on this surface
Re-run with --force to actually drain. Use --void <finalizer> to void one instead.

A cleanup step that another process took less than its lease time ago is reported as held. The lease here was 30 seconds:

• [0] release: lease held (expires 2026-10-01T23:01:37.538Z) — a drainer is executing NOW

With --force while that lease has not passed, the pass stops at that step and runs nothing more: it prints ⚠ finalizer '<name>' left pending — held by another drainer's lease until <time> (realm cannot tell whether it is still running), then Cleanup step left pending: '<name>' — held by another drainer's lease until <time> (realm cannot tell whether it is still running) — after <time>: realm run drain <id> --force, and exits 1 — never has no pending finalizers. After that time the command runs it. Added after version 0.46.0.

With --force:

Drained run '64e1c3a6-5882-4955-a796-a03355fe97f4'.

With --void:

finalizer 'release' voided by operator — may have executed without a recorded mark (its lease had already expired)

With --all, and then with --all --force:

3 run(s) WOULD be drained:
• 1bdb1307-af8b-4016-8189-f054621411b9
• 41a348b2-e48e-48ee-8622-21df7135688e
• 64e1c3a6-5882-4955-a796-a03355fe97f4
Re-run with --force to actually drain them.
✓ 1bdb1307-af8b-4016-8189-f054621411b9: drained
Drained 1/1 run(s).

With --expired, a gate whose time has passed is reported first, with the choice it would be settled with. If the settled choice would make a guard step ready, the report says what the guard would then do: would pass, would pass and complete the run, would then abort the run (<reason>) or would then fail the run (<reason>). Nothing is written:

Run '0e0ace7e-e57c-400c-a63a-eaf685688d19': gate expired 18s ago — would enact settle_default 'hold'; guard 'only_if_shipping' would then abort the run (The order was held.) on --force.
Run '230b0939-9c61-40e4-8b6f-910594a81e92': gate expired 17s ago — would enact settle_default 'ship'; guard 'only_if_shipping' would pass on --force.

The report leaves the guard out when the run’s registered workflow cannot be read.

With --expired --force, the gate is carried out. The same write decides the guard, and its lines follow. Then the run’s cleanup steps are drained as usual:

✓ gate enacted (settle_default 'hold').
Guard step 'only_if_shipping' aborted the run.
Reason: The order was held.
Run '0e0ace7e-e57c-400c-a63a-eaf685688d19' has no pending finalizers. Nothing to drain.
✓ gate enacted (settle_default 'ship').
Guard step 'only_if_shipping' passed.
Run '230b0939-9c61-40e4-8b6f-910594a81e92' is not terminal (phase: 'running') — nothing further to drain.
An agent step is ready: 'ship' — drive it with realm agent --run-id 230b0939-9c61-40e4-8b6f-910594a81e92 --provider <provider> --model <model>

When the enacted gate leaves auto steps only the engine runs, one more line names them. Added after version 0.46.0:

✓ gate enacted (settle_default 'approve').
Run 'daeede5e-c0dd-4b88-9caf-6efa089902dd' is not terminal (phase: 'running') — nothing further to drain.
To run the step the engine owes ('after'): realm run advance daeede5e-c0dd-4b88-9caf-6efa089902dd

When it leaves nothing that can run from here, the steps that cannot run and the way out follow instead. Added after version 0.46.0:

✓ gate enacted (settle_default 'approve').
Run '1d703406-777f-4bc6-a6bc-6058b4d8f490' is not terminal (phase: 'running') — nothing further to drain.
'compute' cannot run (input_schema): Invalid input for step 'compute': the input must have required property 'n'.
Run 1d703406-777f-4bc6-a6bc-6058b4d8f490 stays open (phase 'running'): for 'compute', the engine gives it no input, so correct its input_schema and register the workflow again; then, after a fix, realm run advance 1d703406-777f-4bc6-a6bc-6058b4d8f490 — or end it: realm run abandon 1d703406-777f-4bc6-a6bc-6058b4d8f490

When it leaves no work for the engine and nothing that cannot run, but an agent step ready or a step in flight in another program, the line realm run advance prints for it follows (the guard that passed above leaves the agent step ship ready). Added after version 0.46.0, which prints nothing after nothing further to drain. there:

✓ gate enacted (settle_default 'approve').
Run '7d2e9b14-5a3c-4f81-b0e6-93c4a1f2d857' is not terminal (phase: 'running') — nothing further to drain.
'pack' is in flight in another program — wait for it, or see realm run inspect 7d2e9b14-5a3c-4f81-b0e6-93c4a1f2d857

With --all --expired, the list names what each gate declared, without the choice or the guard. With --force, what each guard did is printed under its run:

2 run(s) WOULD be drained:
• 962cb7f0-a894-4592-aadc-a4907ef14c9c: gate expired 41s ago — would enact settle_default
• ce8296d1-c5d3-4ea6-b62c-7dc0f795fb09: gate expired 40s ago — would enact settle_default
Re-run with --force to actually drain them.
✓ 962cb7f0-a894-4592-aadc-a4907ef14c9c: gate enacted
Guard step 'only_if_shipping' passed.
✓ ce8296d1-c5d3-4ea6-b62c-7dc0f795fb09: gate enacted
Guard step 'only_if_shipping' aborted the run.
Reason: The order was held.
Drained 2/2 run(s).

When there is nothing to do, it prints one of:

Run '03431f4f-7b71-4ad0-98b1-f1d51cc5c4c8' has no pending finalizers. Nothing to drain.
Run 'cba9901c-fa22-47dd-97e2-47439238d01f' is not terminal (phase: 'running') — nothing to drain. To end the run: realm run abandon cba9901c-fa22-47dd-97e2-47439238d01f
Run 'daeede5e-c0dd-4b88-9caf-6efa089902dd' is not terminal (phase: 'running') — nothing to drain. To run the step the engine owes ('after'): realm run advance daeede5e-c0dd-4b88-9caf-6efa089902dd
To end the run instead: realm run abandon daeede5e-c0dd-4b88-9caf-6efa089902dd
Run '573ff99d-44fc-42c9-98e8-c394fed45e6e' is not terminal (phase: 'running') — nothing to drain. 'compute' cannot run (input_schema): Invalid input for step 'compute': the input must have required property 'n'. Run 573ff99d-44fc-42c9-98e8-c394fed45e6e stays open (phase 'running'): for 'compute', start a run with params that fit, or correct its input_schema and register the workflow again; then, after a fix, realm run advance 573ff99d-44fc-42c9-98e8-c394fed45e6e — or end it: realm run abandon 573ff99d-44fc-42c9-98e8-c394fed45e6e
Run 'e1a7c2b4-6d90-4f3e-8b25-0c7d9e1f4a68' is not terminal (phase: 'running') — nothing to drain. An agent step is ready: 'write' — drive it with realm agent --run-id e1a7c2b4-6d90-4f3e-8b25-0c7d9e1f4a68 --provider <provider> --model <model>
To end the run instead: realm run abandon e1a7c2b4-6d90-4f3e-8b25-0c7d9e1f4a68
Run '5b3f90de-2c47-4a18-9e6d-f81a0b7c3d25' is not terminal (phase: 'running') — nothing to drain. 'fetch' is in flight in another program — wait for it, or see realm run inspect 5b3f90de-2c47-4a18-9e6d-f81a0b7c3d25
To end the run instead: realm run abandon 5b3f90de-2c47-4a18-9e6d-f81a0b7c3d25
Run '22efc6a7-01f8-4256-9d2f-74621b621d28' is not terminal (phase: 'gate_waiting') — nothing to drain. To go on, answer its question first: realm run respond 22efc6a7-01f8-4256-9d2f-74621b621d28 --gate 817f3921-6ddd-4bda-9506-762892ae37e7 --choice <one of: approve, reject>
The answer can end the run by itself; if it does not, realm run respond names what the run owes next, and realm run abandon 22efc6a7-01f8-4256-9d2f-74621b621d28 ends it.
Run '94bf33c8-3933-47c4-ad50-556d0b298e6c' is not terminal (phase: 'gate_waiting') — nothing to drain. Its gate expired 25s ago. To see what the expiry will do: realm run drain 94bf33c8-3933-47c4-ad50-556d0b298e6c --expired
To carry it out: realm run drain 94bf33c8-3933-47c4-ad50-556d0b298e6c --expired --force
No runs with an actionable pending finalizer.

The runs after the second — the work the engine owes, a step that cannot run and the way out, an agent step that is ready, a step in flight in another program, each with the way out after it — were added after version 0.46.0, which prints the second line’s To end the run: realm run abandon <id> for every run in running. The second line is now for a run with none of these. A command ends its line: where the way out follows it, it is the next line (added after version 0.46.0, which printed them on one line, the command followed by a full stop). At a question, the second line says that after the answer realm run respond names what the run owes next (added after version 0.46.0, which named only realm run abandon for a run still open after the answer).

Exit code: 0 if every cleanup step it tried ran, and when there is nothing to do. 1 if a cleanup step is left owed after --force, if --force prints one of the seven is not terminal … nothing to drain lines above, or for one of:

Provide a <run-id>, or use --all for batch mode.
Finalizer 'release' is 'voided', not 'pending' — nothing to void.
No finalizer ledger entry named 'nope' on run '41a348b2-e48e-48ee-8622-21df7135688e'.
Finalizer 'release' has an active drain lease (expires 2026-10-01T23:02:59.535Z) — a drainer is executing NOW. Wait for the lease to expire (bounded — leases are clamped to 300s) and retry. Not force-bypassable.
realm run purge <run-id> [--force]
realm run purge --older-than <duration> [--workflow <id>] [--force]

Deletes finished runs, with their refused answers and trace entries. A deleted run cannot be brought back. Runs that have not ended are never deleted.

Flag What it does
--force Deletes. Without it, purge lists what it would delete.
--older-than <duration> Selects every finished run that has not changed for this long.
--workflow <id> With --older-than: only runs of this workflow.
Terminal window
realm run purge --older-than 30d
3 run(s) WOULD be purged (7.2 KB to free):
• 08c61d5d-8db8-4556-87e4-0ba4cc76dc6c (failed, 2.4 KB)
• 5ef64237-9f2d-4624-84c8-9686feb38998 (completed, 2.4 KB)
• d0375dc9-35f5-462b-b12c-f145b404f276 (abandoned, 2.5 KB)
1 of 3 selected run(s) are resumable via 'realm run resume' — purging would destroy that path permanently.
Re-run with --force to actually delete.

With --force:

Purged 2/3 run(s) (4.9 KB freed, store-reported). 0 already gone, 1 blocked, 0 failed.
⚠ 5ef64237-9f2d-4624-84c8-9686feb38998: drain_pending
1 of 3 selected run(s) were resumable via 'realm run resume' — purging has destroyed that path for them.
Count Means
Purged Runs deleted, of the runs selected.
already gone Runs that something else deleted first.
blocked Runs that were left in place. drain_pending means the run still owes a cleanup step; see drain.
failed Runs that could not be deleted because of an error.

The line about realm run resume counts the selected runs that had a failed step resume could have made runnable again: a failed cleanup step is not counted, since resume --from refuses it. A run whose workflow cannot be read is counted when it has any failed step: resume refuses it until the workflow is registered again, and only the workflow tells a cleanup step from another.

Given a run ID in place of --older-than, purge works on that one run:

1 run(s) WOULD be purged (2.7 KB to free):
• 03431f4f-7b71-4ad0-98b1-f1d51cc5c4c8 (completed, 2.7 KB)
None of the 1 selected run(s) are resumable via 'realm run resume'.
Re-run with --force to actually delete.

When no run is old enough, it prints No eligible runs found to purge.

Exit code: 0, also when a run was blocked or nothing matched. 1 if a run could not be deleted because of an error, or for one of:

Provide a <run-id> to purge, or use --older-than for batch mode.
Cannot combine a <run-id> with --older-than. Use one or the other.
Refusing to purge 'f78b61ba-3464-4802-8bc0-03fcaaeebb8d': not terminal (phase: 'gate_waiting').
realm run gc --older-than <duration> [--heal] [--force]
realm run gc --heal [--force]

Removes two kinds of file that a crash can leave in ~/.realm/runs/: temporary files from a write that was cut off, and trace entries whose run no longer exists. With --heal, it also corrects run records whose stored phase differs from the phase their contents give.

Flag What it does
--older-than <duration> Only files at least this old. The least it accepts is 1h. Required unless --heal is the only thing asked for.
--force Deletes and rewrites. Without it, gc lists what it would do.
--heal Also corrects stored phases. No age limit applies to this part.
Terminal window
realm run gc --older-than 1d
1 orphaned .tmp file(s) WOULD be reaped (2.4 KB to free):
• ~/.realm/runs/08c61d5d-8db8-4556-87e4-0ba4cc76dc6c.json.4242.9f3c2a1b.tmp
Re-run with --force to actually delete.
1 run-less orphaned artifact(s) WOULD be reaped:
• ~/.realm/runs/trace-buffer-fbe2f08b-b5e2-435b-ac41-77b0be623b33-d29yaw.jsonl (run fbe2f08b-b5e2-435b-ac41-77b0be623b33)
Re-run with --force to actually delete.
gc does NOT yet reap orphaned .lock dirs (deferred — issue #164). Their presence in runsDir is expected and not a sign gc is broken.

On version 0.45.0 the part after the process id is a counter instead of 8 random characters: ….json.4242.1.tmp. gc removes both forms.

With --force:

Reaped 1 orphaned .tmp file(s) (2.4 KB freed). 0 already gone, 0 failed.
Reaped 1 run-less orphaned artifact(s). 0 already gone, 0 failed.
• ~/.realm/runs/trace-buffer-fbe2f08b-b5e2-435b-ac41-77b0be623b33-d29yaw.jsonl (run fbe2f08b-b5e2-435b-ac41-77b0be623b33)

Both forms end with the same note about lock folders. On a store with nothing to remove:

No orphaned .tmp files found to reap.
No run-less orphaned WAL/sidecar artifacts found to reap.
gc does NOT yet reap orphaned .lock dirs (deferred — issue #164). Their presence in runsDir is expected and not a sign gc is broken.

With --heal, on a store with nothing to correct:

No stale-phase records found to heal.

Correcting a record sets its time of last change to now. realm run purge --older-than and realm run cleanup --older-than read that time.

Exit code: 0. 1 if a file could not be read or deleted, or for:

--older-than must be at least 1h (got '30m'). gc refuses to reap crash residue younger than that, even with --force.
realm run reconcile [--workflow <id>] [--dry-run]

Realm keeps one small file for each idempotency key, in ~/.realm/runs/keys/, that names the run holding the key. reconcile writes any of these files that is missing or out of date, from the runs themselves.

Flag What it does
--workflow <id> Only runs of this workflow.
--dry-run Reports what would be written and writes nothing.
Terminal window
realm run reconcile --dry-run
Would write 1 pointer(s) across 1 key group(s); 0 already current.

Without --dry-run, the line starts with Wrote. Run again, it prints Wrote 0 pointer(s) across 1 key group(s); 1 already current. A key group is one key of one workflow. See Idempotency and batches for a store where two runs hold one key.

Exit code: 0.

realm run migrate --stamp-seals [--force] [--detailed-exitcode]

Since version 0.39, a run that ends records what ended it, which realm run inspect prints as Sealed by. migrate --stamp-seals works out that value for finished runs written by older versions and stores it. It does not change a run’s time of last change.

Flag Required What it does
--stamp-seals Yes The only kind of migration.
--force No Writes. Without it, migrate lists what it would write.
--detailed-exitcode No Exits with 2 if a finished run is left whose ending cannot be worked out, or whose record disagrees with its stored ending.

On two runs written by version 0.38:

Terminal window
realm run migrate --stamp-seals
2 run(s) WOULD be stamped:
• 9a45ca3c-2df1-4daf-ac77-832344422fed: complete (phase completed)
• c9334380-2d09-463e-96c1-2a98f98a66c9: abandon_requested (phase abandoned)
Re-run with --force to actually stamp.
Residue: 2 terminal run(s) still without a recorded seal arm.
Run `realm run migrate --stamp-seals` BEFORE `realm run gc --heal` after upgrading across #367 if retention clocks matter: heal rewrites the same records and resets updated_at, which this command deliberately preserves.

With --force, the first lines become:

Stamped 2 run(s) with their seal arm.
• 9a45ca3c-2df1-4daf-ac77-832344422fed: complete (phase completed)
• c9334380-2d09-463e-96c1-2a98f98a66c9: abandon_requested (phase abandoned)
Residue: 0 terminal run(s) still without a recorded seal arm.

A run stamped in this way shows Sealed by: complete (recovered by classifier) in realm run inspect.

When every finished run has the value already:

Nothing would be stamped — every terminal run already carries its seal arm.
3 run(s) already stamped, and their arms agree with the record.
Residue: 0 terminal run(s) still without a recorded seal arm.

Exit code: 0. 1 if a write failed, or if --stamp-seals was not given. 2 only with --detailed-exitcode, as described above.