Generate a Rust CLI
The generator derives entity and operation subcommands from validated definitions, writes a Rust
crate that uses clap derive, and compiles it for the host. The command embeds the definitions,
validates them on startup and runs stored operations through StoredRuntime on a File Store. It
opens no HTTP server and selects no database.
Prepare a matching source checkout
Generation needs Git, Rust and Cargo, a source checkout of the same Entity Runtime version as the
entity you run, and that checkout's dependencies in Cargo's cache. In a fresh directory:
git clone --depth 1 --branch 0.27.0 \
https://github.com/beyond10x/entity-runtime.git runtime-source
cargo fetch --manifest-path runtime-source/Cargo.toml --locked
cp runtime-source/examples/refund.yaml refund.yaml
Fetching is the explicit network step. Generation itself runs Cargo with --locked --offline.
Build the command
entity generate rust-cli \
--definition refund.yaml \
--name refundctl \
--out ./bin/refundctl \
--runtime-source ./runtime-source
It prints Cargo's build output, then the binary and the retained source (shown with the working
directory abbreviated to …):
generated refundctl at ./bin/refundctl; source retained at …/build/entity-runtime/refundctl
The crate and its lockfile stay under build/entity-runtime/refundctl (--build-dir moves them)
so the binary can be rebuilt. The generator builds into that directory's own target and installs
the executable Cargo reports, so CARGO_TARGET_DIR or a .cargo/config.toml override changes
neither where the build lands nor where the result is found.
Run a refund through it
The actor names, roles and timestamps are sample data; in a real tool handler trusted code derives them.
./bin/refundctl --store ./refund-store refund create \
--id refund-104 \
--fields '{"order_id":"order-88","amount_cents":12500,"evidence_count":2}' \
--record-id request-104-created --recorded-at 2026-08-31T10:00:00Z \
--actor support-api > created.json
./bin/refundctl --store ./refund-store refund submit \
--id refund-104 --expected-revision 1 \
--record-id request-104-submitted --recorded-at 2026-08-31T10:01:00Z \
--actor support-agent > submitted.json
./bin/refundctl --store ./refund-store refund approve \
--id refund-104 --expected-revision 2 \
--arguments '{"actor_role":"human","reason":"supervisor verified the evidence"}' \
--record-id request-104-approved --recorded-at 2026-08-31T10:04:00Z \
--actor supervisor-7 > approved.json
$ ./bin/refundctl --store ./refund-store refund get --id refund-104 --format text
refund refund-104 is approved (revision 3)
$ ./bin/refundctl --store ./refund-store refund list
refund-104
$ ./bin/refundctl --store ./refund-store refund events --id refund-104 | jq -c '.[] | {type: .type, revision}'
{"type":"RefundDrafted","revision":1}
{"type":"RefundSubmitted","revision":2}
{"type":"RefundApproved","revision":3}
Every operation subcommand requires --expected-revision; get reads the current instance and
events the emitted domain events, not the full history.
Recover an accepted request
Repeat the approval exactly — same revision, arguments and recording — and the original commit comes back; a new request on the old revision is refused:
$ ./bin/refundctl --store ./refund-store refund approve \
--id refund-104 --expected-revision 2 \
--arguments '{"actor_role":"human","reason":"supervisor verified the evidence"}' \
--record-id request-104-approved --recorded-at 2026-08-31T10:04:00Z \
--actor supervisor-7 > retried.json
$ cmp approved.json retried.json && echo identical
identical
$ ./bin/refundctl --store ./refund-store refund reject \
--id refund-104 --expected-revision 2 --arguments '{"reason":"too late"}' \
--record-id request-104-rejected --recorded-at 2026-08-31T10:05:00Z --actor supervisor-7
refused: refund refund-104: expected revision 2, found revision 3
The refusal exits 1. ./bin/refundctl refund approve --help lists the generated flags.
Regenerate
--force replaces only the exact output binary and a source directory that carries the
generator's marker. Editing the YAML beside an existing binary changes nothing: regenerate to
change the embedded model.