relayburn-cli 2.3.0

The `burn` CLI — published to crates.io. Crate name is relayburn-cli because `burn` is taken on crates.io; the binary keeps the `burn` invocation.
Documentation
[package]
name = "relayburn-cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
description = "The `burn` CLI — published to crates.io. Crate name is relayburn-cli because `burn` is taken on crates.io; the binary keeps the `burn` invocation."

[[bin]]
name = "burn"
path = "src/main.rs"

# A library target is added alongside the `burn` binary so the harness
# substrate (`HarnessAdapter` trait, registry, pending-stamp factory)
# under `src/harnesses/` can be unit-tested with `cargo test -p
# relayburn-cli` and so future integration tests under `tests/` can
# reach it without re-declaring the module tree. The binary path
# (`src/main.rs`) and the library path (`src/lib.rs`) live side-by-side
# — cargo treats them as two separate compile units that share the
# same package metadata.
[lib]
name = "relayburn_cli"
path = "src/lib.rs"

[dependencies]
# The CLI is the canonical external embedder of the SDK — every read
# verb the binary surfaces will wrap a `relayburn-sdk` call once #248
# fills in the CLI source.
#
# Version requirement is the current MAJOR.MINOR (caret semantics:
# `>=1.10.0, <2.0.0`). The publish workflow rewrites this to match
# the workspace MAJOR.MINOR on every release so the local workspace
# path (currently 1.10.0) and the published `relayburn-sdk` on
# crates.io always satisfy the dep at publish time.
relayburn-sdk = { path = "../relayburn-sdk", version = "2.3" }

# clap v4 derive — argument parsing root and subcommand dispatch. The
# scaffold defines globals + subcommand stubs only; per-command flag
# wiring lands in the Wave 2 fan-out PRs.
clap = { workspace = true }

# `comfy-table` is used by the shared table renderer in `render::table`.
# Picked over `tabled` because it ships with sane Unicode/ASCII presets
# and a simple builder API that maps cleanly onto a `Vec<Vec<String>>`
# of rows. Wave 2 commands render tabular output through this helper.
comfy-table = "7"

# Used by `render::json` to emit the structured-output mode the TS CLI's
# `--json` global produces. `serde` derive lives here too because the
# rendering helpers are generic over `Serialize` types from the SDK.
#
# `preserve_order` keeps insertion order on `serde_json::Map` so the
# pretty-printed JSON in `commands/overhead.rs` (and any future verb that
# round-trips through `to_value` / `to_writer_pretty`) matches the TS
# CLI's `JSON.stringify` output byte-for-byte. The SDK already enables
# this feature; cargo's feature unification means the CLI inherits it
# transitively today, but pinning it here is defense-in-depth so a
# future SDK refactor can't silently regress the golden snapshots.
serde = { workspace = true }
serde_json = { workspace = true, features = ["preserve_order"] }

# `anyhow` for the binary entrypoint; typed errors flow through
# `relayburn_sdk::LedgerError` and friends. `render::error::report_error`
# does the SDK-error → stderr/exit-code mapping.
anyhow = { workspace = true }
thiserror = { workspace = true }

# Harness substrate (#248-b): the `HarnessAdapter` trait under `harnesses/`
# uses `async fn` in trait — `async-trait` desugars to `Pin<Box<…>>` futures
# without forcing every adapter to spell that out. `phf` macros give us a
# perfect-hash registry that's evaluated at compile time, so harness lookup
# costs nothing at startup. `tokio::sync` is needed for the watch controller
# wiring exposed by `relayburn-sdk` (`WatchController` holds a `Mutex`).
# `rt` is required by Wave 2 read-path commands so they can drive
# `relayburn_sdk::ingest_all` (async) from the otherwise-sync presenter
# bodies via a current-thread runtime.
async-trait = "0.1"
phf = { version = "0.11", features = ["macros"] }
# `process` is needed by `commands/run.rs` so the `burn run` driver can
# `await` the child via `tokio::process::Command::status()` rather than
# blocking the current-thread runtime with `std::process::Command::status()`.
# `signal` is needed for the `burn ingest --watch` SIGINT/SIGTERM trap
# (#248 D8); the watch loop blocks the foreground until a stop signal
# comes in. `rt` drives the current-thread runtime that wraps the SDK's
# async ingest verb from otherwise-sync presenter bodies.
tokio = { workspace = true, features = ["sync", "rt", "process", "signal"] }

# `IndexMap` preserves first-seen iteration order, which matters for the
# Wave 2 read-path commands so their grouped output (`summary --by-model`,
# the per-cell fidelity block) ties cost-sorts the same way the TS CLI's
# `Map`-backed implementation does. Mirrors the SDK's own dependency.
indexmap = { version = "2", features = ["serde"] }

[dev-dependencies]
# `assert_cmd` drives the binary in the smoke test under `tests/`.
# `predicates` provides the matchers `assert_cmd` wants.
assert_cmd = "2"
predicates = "3"

# Async test harness for the `harnesses` module unit tests (lookup, factory
# round-trips). `rt-multi-thread` + `macros` lets `#[tokio::test]` resolve
# and gives spawned watch-loop ticks a runtime to land on.
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "sync"] }

# `tests/golden.rs` loads `invocations.json` to drive both the TS-CLI snapshot
# capture and the Rust diff runner. `serde` / `serde_json` are workspace deps
# already pulled in via `[dependencies]`, so this entry is documentation only.
#
# The in-tree fixture is JSONL-only (the SQLite binaries are gitignored); the
# diff runner just deletes any prior sqlite before invoking the binary, and
# the SDK's `Ledger::open` rebuilds `burn.sqlite` from `ledger.jsonl`
# automatically (see `relayburn_sdk::ledger::bootstrap`). No JSONL parsing in
# the test helper itself.