On this page

Plan fleet expansion and payback

stado fleet expansion compares complete expansion options against current fleet needs, selects the greatest incremental cash gain within a total expenditure budget, and saves the evidence and result. It never purchases hardware, rents a machine, enrolls a host or moves workloads.

Commands

stado fleet needs --days 7 --json
stado fleet expansion catalog --json
stado fleet expansion set --document - --json
stado fleet expansion plan --budget-usd 10000 --horizon-months 24 --days 7 --json
stado fleet expansion history --json
stado fleet expansion show <plan-id> --json

set --document - reads JSON from standard input; --document JSON accepts one argument. The first catalog needs no expected version. Replacement requires --expect-version VERSION from catalog; missing or stale versions refuse without overwriting. Empty options clears the catalog, never saved plans.

Catalog and evidence

A catalog is { "schema_version": 1, "options": [...] }. Each option is one complete bundle, selected at most once. Quantities and dependencies belong in complete alternative bundles, not assumptions silently multiplied by the tool.

Field Meaning
id, label Unique lowercase identifier and readable description.
kind buy, upgrade, rent, reclaim or relocate.
need_keys Current storage:<target>, cpu:<target>, host:<platform> or gpu:fleet keys: the kinds stado fleet needs reports. Any other kind, ram included, is refused, because no need would ever match it.
benefit_group Options using the same avoided spending or revenue share this group.
upfront_usd Incremental acquisition/setup cost, including applicable tax and delivery.
monthly_cost_usd New power, hosting, maintenance, rental and other cash costs.
monthly_savings_usd Cash spending avoided; unused cloud credits are not a cash saving.
monthly_margin_usd Additional contribution after other delivery costs, not gross revenue. Do not also count it as savings.
lead_time_days Delay until benefits begin; operating costs begin immediately.
evidence Source references and basis for costs, capacity improvement and monetized benefits.
observed_at, valid_until RFC3339 observation and expiry of these assumptions.

All four money fields accept null for unknown. Zero means an explicitly known zero. Incomplete options cannot be selected. The planner never fills prices from a zero cost forecast. Sources remain declared assumptions, not verified quotes or guaranteed returns. CPU labels can reflect reservations rather than actual processor saturation; occupied swap alone does not prove more RAM will help.

Money is finite, nonnegative, specified to cents, and no larger than whole cents a 64-bit integer holds. The horizon is required and at least one month; nothing assumes one for you. The needs window is at least one day. Every option is considered: the exact selection prunes only branches that cannot beat the best portfolio found. Identifiers contain lowercase letters, digits, underscores or hyphens; text fields must not be empty. Duplicate IDs/need keys, malformed dates and unsupported schemas refuse. Future observations and expired evidence make an option unevidenced; absent current needs and over-budget options make it excluded; an unknown money field leaves it unknown. Only eligible options are selected, and every other one remains visible with its reasons. Any unknown or unevidenced option, or none at all, makes an unselected plan insufficient_evidence.

Financial meaning

Let U be upfront cost, C monthly cost, B savings plus additional margin, H months and L delivery days divided by 30.4375.

  • Total expenditure: U + C × H. The budget covers BOTH; savings do not finance purchases. This is not an autonomy spending limit.
  • Monthly net benefit after delivery: B − C.
  • Horizon gain: B × max(0, H − L) − U − C × H.
  • Payback: L + (U + C × L) / (B − C) if B > C; otherwise no finite payback.
  • Horizon ROI: 100 × horizon gain / total expenditure. Zero expenditure makes ROI undefined (null), not infinite.

Portfolio payback uses combined cashflows at each delivery date, not averaged option paybacks. Projections are undiscounted and assume fixed monthly amounts; financing, resale value, inflation and changing demand are not inferred. Time saved is not valued unless its cash effect has an explicit evidence source.

Search is exact over the declared bundles, not greedy payback/price ranking. Options sharing ANY need key or benefit group cannot be selected together, to avoid counting one workload benefit twice. Only positive horizon gains qualify. The empty portfolio is always available. Equal gains prefer lower expenditure, then deterministic identifier ordering. This is conditional optimality among supplied assumptions, not a claim about every product on the market.

Illustrative input

This is a financial scenario, NOT a vendor quote or a purchase recommendation. It requires an actual host:darwin-arm64 need and current evidence to qualify. Expired example dates make it unevidenced; they are not silently refreshed.

{
  "schema_version": 1,
  "options": [{
    "id": "mac-scenario",
    "label": "One complete macOS worker scenario",
    "kind": "buy",
    "need_keys": ["host:darwin-arm64"],
    "benefit_group": "macos-build-spend",
    "upfront_usd": 6000,
    "monthly_cost_usd": 50,
    "monthly_savings_usd": 650,
    "monthly_margin_usd": 0,
    "lead_time_days": 0,
    "evidence": "Illustrative assumptions only; not a quote or measured earnings",
    "observed_at": "2026-09-21T00:00:00Z",
    "valid_until": "2026-09-28T00:00:00Z"
  }]
}

Over 24 months these assumptions mean 7200 USD expenditure, 8400 USD gain and 10-month payback. They do not establish that the existing fleet earns this money.

Persistence and refusals

Catalog: state/fleet/expansion/catalog.json, versioned with compare-and-swap. Plans: state/fleet/expansion/plans/<uuid>.json, immutable, verified by read-back. A plan includes original needs, full option assumptions, catalog and Stado versions, timestamps, exclusions and portfolio. show and history do not recalculate historical reports using new prices.

Status Meaning
ready An eligible portfolio was selected; budget may still leave needs unaddressed.
partial A portfolio was selected but some needs have no eligible evidenced option.
insufficient_evidence Missing/stale assumptions prevent selecting a portfolio.
no_viable_option No positive-gain eligible portfolio fits.
no_needs No unmet need was reported; this does not prove complete telemetry.

plan exits nonzero for partial, insufficient_evidence and no_viable_option, AFTER saving and printing its report. Successful catalog, set, show and history operations exit zero. Invalid money, invalid UUID, version conflicts and store failures name the failed operation. Unreadable storage never becomes an empty catalog or zero cost.

Stado Desktop

Fleets → Plan expansion… uses the selected endpoint's operator API. The screen has budget/horizon/window fields, current need keys, an option form, versioned saving, candidate reasons, portfolio economics and saved history. Blank money remains unknown. Keep in draft edits locally; Save catalog persists assumptions; Save plan uses the saved catalog, not unsaved drafts. Reload asks before discarding edits. A version conflict preserves the draft. Incomplete reports display even when their command exits nonzero. Changing endpoint clears old results. The app never shells out to a local CLI.

Measured costs, autonomy and machine enrollment remain separate capabilities.

Separate batch qualification

Pushing planner changes does not run a private build for that coding session. The release manifest declares no post-build test for the planner: no test path has been approved with tama tests approve, so none runs. A planner change qualifies on the passing build of each required platform.

The task handoff and its later batch verdict are separate records:

oko-cli transcripts tasks submit <task-id> --source <canonical-repository> --commit <full-pushed-commit> --by <session-id>
oko-cli transcripts tasks show <task-id> --json
oko-cli transcripts tasks qualification --json
stado release changes list --task <task-id> --json

Submission records pushed work without scheduling a build. A later shared build binds only changes whose commits it contains. A failed build reopens the associated coding task. An installed CLI that does not yet recognize these commands has not received this source change.

Source: this website