--json now emits a stable sizing:{version:1,arch,nodes,cores,ram_gb,est_makespan_min}
block reusing provision.arch_for_system + estimate.peak_ram_gb_per_node. render_ephemeral_builders
now derives requirements.maxDuration + parking + policy from est_makespan_min for class ci.
The --provision renderer defaulted ramGb to 2 GiB/core. Since #15 the
scheduler tracks per-derivation RAM, so compute the peak concurrent RAM on
the busiest node of the recommended shape (peak_ram_gb_per_node) and feed it
into each EphemeralBuilder's requirements.ramGb. Explicit --node-ram-gb still
overrides; a nothing-to-build closure falls back to the per-core default.
Emit N documents (one per node; no replica field) using the real
arbitrage.oleks.space/v1alpha1 schema: top-level class + requirements
{arch,cores,ramGb}, namespace builder-arbitrage, class label. Rename
public API to render_ephemeral_builders; wire --node-ram-gb into ramGb.
_recommend assumes sorted, deduped grids (core_grid[-1] = biggest, knee
sweeps run low->high). The CLI parsed --nodes/--cores in raw order, so
e.g. --cores 32,8,16 yielded nonsense. Sort+dedupe in cli.py and reject
non-positive/non-integer values; also normalize defensively inside
estimate() so the function is correct for any caller. Add estimate/cli
tests.