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)ifB > 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