# lean-rs-worker

Worker-process boundary for `lean-rs` host workloads. Supervises a `lean-rs-worker-child` process around the in-process
theorem-prover stack in [`lean-rs-host`](https://docs.rs/lean-rs-host), adding fatal-exit containment, memory cycling,
request timeouts, and parent-owned row streaming.

Use this crate when a downstream tool needs process isolation, live rows, diagnostics, terminal summaries, request
timeouts, or memory cycling. Use `lean-rs-host` directly for trusted in-process work that does not need those
guarantees.

## Quick start in this repo

```sh
cargo run -p lean-rs-worker --example worker_capability_runner
```

The runner is the normal downstream shape: `LeanWorkerCapabilityBuilder` manages startup, typed commands carry requests
and rows, diagnostics arrive on a separate sink, and explicit cycling resets process-global memory. The full recipe is
[`docs/recipes/worker-capability-runner.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/recipes/worker-capability-runner.md).

This in-tree example uses the workspace worker child. It is useful for learning the API and for testing `lean-rs-worker`
itself.

## Ship your own worker app

For a shipped application, build the Lean capability in `build.rs` with `lean_toolchain::CargoLeanCapability`, embed the
resulting path with `lean_rs::LeanBuiltCapability::manifest_path(env!(...))`, and ship an app-owned worker child binary:

```rust,ignore
fn main() -> std::process::ExitCode {
    lean_rs_worker::run_worker_child_stdio()
}
```

Point the builder at that binary with
`LeanWorkerChild::sibling("my_app_lean_worker").env_override("MY_APP_LEAN_WORKER")`. Run
`LeanWorkerCapabilityBuilder::check()` in installers, startup probes, or doctor commands to validate the child binary,
manifest-backed capability, protocol handshake, import session, and optional metadata expectation before real work
starts. This is the production packaging path because Cargo does not install dependency binaries as part of your
application. See
[`docs/recipes/ship-crate-with-lean.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/recipes/ship-crate-with-lean.md).

## What the crate owns

| Concern | API |
| --- | --- |
| Child process lifecycle, pipes, frame decoding, fatal-exit classification, cleanup | `LeanWorker`, `LeanWorkerConfig`, `LeanWorkerError`, `LeanWorkerExit`, `LeanWorkerStats` |
| Restart and memory-cycling policy: explicit cycling, request-count threshold, import-like threshold, idle interval, RSS ceiling | `LeanWorkerRestartPolicy`, `LeanWorkerRestartReason` |
| Capability startup: Lake build or manifest-backed artifact use, child resolution, bootstrap check, health-check, import session, optional metadata validation | `LeanWorkerCapabilityBuilder`, `LeanWorkerBootstrapReport` |
| Packaged worker child resolution | `LeanWorkerChild`, `run_worker_child_stdio` |
| Typed downstream commands and row streams | `LeanWorkerJsonCommand<Req, Resp>`, `LeanWorkerStreamingCommand<Req, Row, Summary>`, `LeanWorkerTypedDataSink`, `LeanWorkerDiagnosticSink` |
| Narrow host-session adapter (elaboration, kernel-check status, declaration-kind/name bulk queries) | `LeanWorker::open_session` |
| Local multi-worker fanout, warm reuse, fixed admission, RSS-aware policy, lease invalidation | `LeanWorkerPool`, `LeanWorkerSessionLease`, `LeanWorkerSessionKey`, `LeanWorkerPoolSnapshot` |
| Import planning: Lake module discovery to stable pool-execution batches | `LeanWorkerImportPlanner`, `LeanWorkerPlannedBatch` |
| Progress and cancellation across the IPC boundary | `LeanWorkerProgressEvent`, `LeanWorkerError::Cancelled` |

## What the crate does not own

- Downstream command names, request schemas, row schemas, summary schemas.
- Cache validity, ranking, reporting, source provenance, CLI policy.
- Child PIDs, worker IDs, pipes, protocol frames, or which warm worker the pool selected.
- Remote (cross-host) workers, byte callbacks, object callbacks. These are non-goals for the current local-scale
  release; see
  [`docs/architecture/28-production-scale-release.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/architecture/28-production-scale-release.md).

## Key behaviors

**Cycling is the reset boundary for Lean process-global memory.** A `LeanWorkerRestartPolicy` cycles the child before
requests when a configured request count, import-like request count, RSS ceiling, or idle duration is reached.
`SessionPool::drain()` is an in-process cache operation, not an RSS reset.

**Startup and request timeouts are independent.** Startup timeout covers the child handshake. Request timeout covers one
request after its frame is written, including live rows, diagnostics, progress, and terminal response. On
request-timeout expiry the supervisor kills and replaces the child, records `LeanWorkerRestartReason::RequestTimeout`,
returns `LeanWorkerError::Timeout`, and invalidates the open worker session.

**Row delivery uses bounded internal buffering.** Slow sinks block the request path instead of growing memory without
bound. Rows are never dropped for committed streams. Delivered rows are tentative until terminal success returns
`LeanWorkerTypedStreamSummary` with total rows, per-stream counts, elapsed time, and optional typed metadata.

**Large row streams use a raw-JSON payload fast path.** The child validates payload as JSON, the protocol carries it
without building a `serde_json::Value` tree, and typed commands deserialize directly into the caller row type at the
parent boundary. `LeanWorkerDataRow` remains the schema-less escape hatch when callers want arbitrary inspection rather
than maximum throughput.

**RSS sampling is best-effort and platform-specific.** Unavailable samples are recorded as unavailable, not as a false
claim that the pool is under budget.

**Capability metadata and doctor checks are separate from row streams.** `LeanWorker::runtime_metadata` reports protocol
facts from the child handshake. `LeanWorkerSession::capability_metadata` and `LeanWorkerSession::capability_doctor` call
downstream exports with ABI `String -> IO String`, decode the returned JSON into generic command, capability, version,
and diagnostic envelopes, and leave cache policy and semantic command meaning to the downstream tool.

## Lean-side helpers

Lean capability packages should use `LeanRsInterop.Worker.Stream` from `lean-rs-interop-shims` when emitting worker
streams. The helpers build row, diagnostic, progress, terminal-metadata, and status envelopes for the child-local string
callback path. They do not define downstream row schemas, command names, session keys, or pool scheduling policy.

## More examples

| Command | Purpose |
| --- | --- |
| `cargo run -p lean-rs-worker --example worker_streaming` | Typed streaming command with parent-side watchdog and worker cycling. |
| `cargo run --release -p lean-rs-worker --example worker_capability_probe` | Operational probe over generic command shapes (`version`, `doctor`, `extract`, `features`, `index`, `probe`); records cold startup, first import, cancellation latency, fatal-exit recovery, cycling, throughput, RSS. Set `LEAN_RS_WORKER_COMPARE_COMMAND=…` to time a comparison command alongside. |
| `cargo run -p lean-rs-worker --example worker_pool` | Pool, lease acquisition, typed streaming command, leased-worker cycling. |
| `cargo run -p lean-rs-worker --example pool_memory_scheduling` | Pool memory-aware admission and cycling under a small import policy. Records parent RSS, child RSS snapshots, unavailable samples, budget admission, policy restarts. |
| `cargo run -p lean-rs-worker --example mathlib_scale_probe` | Planner → pool → lease → typed command path. Set `LEAN_RS_MATHLIB_ROOT=/path/to/mathlib4` to use a real module list as planning workload. |
| `cargo run -p lean-rs-worker --example lean_dup_readiness` | Readiness fixture over all command shapes. Set `LEAN_RS_LEAN_DUP_ROOT=/path/to/checkout` to record the comparison checkout's revision; set `LEAN_RS_WORKER_COMPARE_COMMAND=…` to time a comparison command. |

The `mathlib_scale_probe` and `pool_memory_scheduling` examples need the worker child built first:
`cargo build -p lean-rs-worker --bin lean-rs-worker-child`.

## Recipes and contracts

- [`docs/recipes/worker-capability-runner.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/recipes/worker-capability-runner.md):
  normal downstream path.
- [`docs/recipes/ship-crate-with-lean.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/recipes/ship-crate-with-lean.md):
  canonical packaged app path with `build.rs`, runtime open helper, and app-owned worker child.
- [`docs/recipes/worker-process-boundary.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/recipes/worker-process-boundary.md):
  lower-level process-boundary recipe.
- [`docs/architecture/28-production-scale-release.md`](https://github.com/jcreinhold/lean-rs/blob/main/docs/architecture/28-production-scale-release.md):
  local scale contract and non-goals.

## License

Dual-licensed under MIT or Apache-2.0 at your option.
