Skip to main content

onebudgetspec

Register measurable budgets in YAML and gate on them. Each budget names a command that performs the measurement, a unit, a direction and a threshold; onebudgetspec check runs every selected command once and reports the actual value, the budget and the headroom.

It ships as a Rust SDK (onebudgetspec-core) and a command line (onebudgetspec), with Python and TypeScript SDKs released beside them at the same version (see SDKs).

Principles

  • Measure once. Each selected budget's command runs exactly once per check. There is no retry, repeat or sampling.
  • Report actual, budget and headroom. Every result carries all three, so even a result within budget shows how close it came.
  • Record host conditions beside each result. The load average, CPU count, available memory and any conditions the file declares are recorded with every measurement, so a person can judge an extreme circumstance from the result itself.
  • Over budget is a defect, never re-sampled. An over-budget result fails the check on any host. It is never re-run until it passes.
  • No baselines. A budget is the threshold written in the file, not a comparison with an earlier run.

Install

pip install onebudgetspec-cli         # or: uvx --from onebudgetspec-cli onebudgetspec
npm install --save-dev @onebudgetspec/cli
cargo install onebudgetspec

The budgets file

A file named budgets.yaml:

schema_version: 1
conditions:            # optional; recorded beside every result measured from this file
  - name: dispatches   # ^[a-z][a-z0-9_]*$, unique, never load1, cpus or mem_available_mib
    command: ["scripts/count-dispatches.sh"]  # run once per file per check; trimmed stdout recorded
budgets:
  - id: gate-time      # ^[a-z][a-z0-9-]*$, unique across every file one discovery finds
    description: "Wall clock of the full gate"  # optional
    labels: [ci]       # optional free tags, for selecting a subset
    measure: reported  # elapsed | reported
    command: ["scripts/budget-gate-time.sh"]  # argv, no shell, run from this file's directory
    unit: seconds      # a free label; `elapsed` requires seconds
    direction: max     # max: within when actual <= threshold; min: within when actual >= threshold
    threshold: 1800    # finite and non-negative
    timeout_seconds: 3600  # optional; past it the measurement is an error

Unknown keys are refused at every level, naming the key and the file.

elapsed times the command's wall clock; a non-zero exit is an error. reported sets ONEBUDGETSPEC_RESULT to the path of a fresh, empty file, and the command writes one JSON object there and exits 0:

{"value": 1395.0, "detail": "optional text", "conditions": {"runner": "large"}}

detail and conditions are optional. Returned conditions are recorded in that result's host conditions only; a returned name equal to a declared condition, load1, cpus or mem_available_mib makes the measurement an error rather than replacing a sampled value. A command's own stdout and stderr go to stderr, never into a report, with one exception: when a budget's command exits non-zero, is ended by a signal or its timeout, or exits 0 with a result that cannot be read, its result's error states that (for an unreadable result, why it could not be read and then the exit status), followed by ; its stderr: and the tail of what the command wrote there. That tail is decoded lossily, each line trimmed, blank lines dropped, the rest joined by | and other control characters made spaces, and it is bounded to at most the last 1000 characters, after a leading … when anything was cut. A command that wrote nothing but whitespace there gets no such suffix; a command that succeeds is reported without its stderr. A failing condition command is still recorded as unknown, and the same reason appears on the line onebudgetspec prints to stderr for it.

The command line

onebudgetspec check [PATH]... [--id ID]... [--label LABEL]... [--exclude-label LABEL]... [--recursive]
onebudgetspec validate [PATH]... [--recursive]
onebudgetspec list [PATH]... [--id ID]... [--label LABEL]... [--exclude-label LABEL]... [--recursive]
onebudgetspec schema

With no PATH, ./budgets.yaml is read. --recursive searches a directory for files named budgets.yaml, honouring .gitignore. Every verb takes --output text|json (--json for short). schema prints the JSON Schema bundle for the budgets file and both reports.

A text result reads:

budget gate-time: actual 1395 seconds, budget 1800 seconds, headroom 405 seconds (22.5%) — within; host: load=2.1/8 mem_available=5120MiB dispatches=3
Exit Meaning
0 every selected budget is within
1 at least one is over, and none errored
2 the invocation or a file is invalid; nothing was run (the reason is on stderr)
3 at least one measurement errored (its reason is that result's error, in the report), or the report could not be written (that reason is on stderr)

The npm launcher adds three of its own, each with the reason on stderr: 64 on a platform no carrier is published for, 69 when the carrier package is missing or its binary cannot run, and 70 when the binary was ended by a signal.

SDKs

Three SDKs, released at the binary's version. The Rust SDK is the engine itself; the Python and TypeScript SDKs run the onebudgetspec binary once per call, parse the JSON it prints, and return the reports as types generated from onebudgetspec schema, so they never drift from it.

Rust Python TypeScript
Package onebudgetspec-core (crates.io) onebudgetspec-sdk (PyPI), imported as onebudgetspec_sdk @onebudgetspec/sdk (npm)
Install cargo add onebudgetspec-core pip install onebudgetspec-sdk, which installs onebudgetspec-cli at the same version npm install @onebudgetspec/sdk, which installs its optional dependency @onebudgetspec/cli at the same version
Calls load(paths, recursive), then .select(&Selection) and .check() or .list_report(), or .all().list_report() to validate; schema_bundle() check(paths=None, ids=None, labels=None, exclude_labels=None, recursive=False, cwd=None) -> CheckReport, validate(paths=None, recursive=False, cwd=None) -> ListReport, list_budgets(...) with check's arguments -> ListReport, schema() -> dict check({paths, ids, labels, excludeLabels, recursive, cwd}), validate({paths, recursive, cwd}), listBudgets({paths, ids, labels, excludeLabels, recursive, cwd}), schema(), each a promise of the generated type
Binary none: it measures in process the binary= argument, then ONEBUDGETSPEC_BIN, then the onebudgetspec the onebudgetspec-cli wheel installed, then onebudgetspec on PATH the binary option, then ONEBUDGETSPEC_BIN, then the launcher of the resolved @onebudgetspec/cli package
Tests and journeys it owes crates/onebudgetspec-core/tests/api.rs, crates/onebudgetspec-core/tests/schema.rs, and every CLI journey and conformance case, which drive it through the binary sdks/python/tests/test_conformance.py (every conformance case through check, list_budgets and validate), sdks/python/tests/test_binary.py (resolution order and refusals), sdks/python/tests/test_generate.py (the model generator), and the packaging journey crates/onebudgetspec-packaging-e2e/tests/packaging/sdk_python.rs sdks/typescript/tests/conformance.test.ts, sdks/typescript/tests/binary.test.ts, sdks/typescript/tests/errors.test.ts and sdks/typescript/tests/generator.test.ts (the same concerns), and the packaging journey crates/onebudgetspec-packaging-e2e/tests/packaging/sdk_typescript.rs

In Python and TypeScript, exit statuses 0, 1 and 3 return the report, since the verdicts are in it; status 2 (an invalid invocation or budgets file) raises OnebudgetspecError (Python) or rejects with it (TypeScript), carrying the CLI's own message and exit_code/exitCode 2.

from onebudgetspec_sdk import check

report = check(labels=["api"], exclude_labels=["slow"], cwd="services")
for result in report.results:
    print(result.id, result.verdict, result.actual, result.headroom, result.host.conditions)
import { check } from "@onebudgetspec/sdk";

const report = await check({ labels: ["api"], excludeLabels: ["slow"], cwd: "services" });
for (const result of report.results) {
  console.log(result.id, result.verdict, result.actual, result.headroom, result.host.conditions);
}

Nesting budgets files

Each project registers its own budgets beside its code, and only the budgets a change can affect run; the budgets that must always be checked go in the root file. With Nx as the worked example, a repository lays them out as:

budgets.yaml               # always checked
services/api/budgets.yaml  # checked when services/api is affected
services/api/project.json

The project's own file:

schema_version: 1
budgets:
  - id: api-cold-start
    description: "Seconds from process start to the first healthy response"
    measure: elapsed
    command: ["scripts/start-and-probe.sh"]
    unit: seconds
    direction: max
    threshold: 2
    timeout_seconds: 30
  - id: api-requests-per-second
    measure: reported
    command: ["scripts/load-test.sh"]
    unit: requests/s
    direction: min
    threshold: 500

checked by that project's budgets target in services/api/project.json:

{
  "targets": {
    "budgets": {
      "command": "onebudgetspec check services/api/budgets.yaml",
      "inputs": ["{projectRoot}/**/*"]
    }
  }
}

and run for the affected projects only:

nx affected -t budgets

The root file holds what every change must stay within:

schema_version: 1
conditions:
  - name: runner
    command: ["uname", "-m"]
budgets:
  - id: gate-time
    description: "Wall clock of the full gate"
    measure: elapsed
    command: ["just", "check"]
    unit: seconds
    direction: max
    threshold: 1800
    timeout_seconds: 3600

checked on every change with onebudgetspec check budgets.yaml. To check every file at once, onebudgetspec check --recursive . discovers both.

Licence

MIT

Metadata

Release files for onebudgetspec-cli 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for onebudgetspec-cli 0.1.1
File
onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_x86_64.whl Python 3 none Linux glibc 2.39+ x86-64 Details
onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_aarch64.whl Python 3 none Linux glibc 2.39+ ARM64 Details
onebudgetspec_cli-0.1.1-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details
onebudgetspec_cli-0.1.1-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details

Total release size: 5.9 MB

Release files / onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_x86_64.whl

Download URL onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_x86_64.whl
Size 1.6 MB
Tags Linux glibc 2.39+ x86-64 Python 3
SHA-256 checksum
How to use checksums
d595b482bac7c123a55c0c314a7a70135809582253260e1e43a9c831e69c669c
BLAKE2b-256 checksum
How to use checksums
7920cb723603145565512159f6729f4d2dedfef170b0a6135977fd7439afeffd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_aarch64.whl

Download URL onebudgetspec_cli-0.1.1-py3-none-manylinux_2_39_aarch64.whl
Size 1.4 MB
Tags Linux glibc 2.39+ ARM64 Python 3
SHA-256 checksum
How to use checksums
46e884a73fb28b8c022b4e1da6abafd847809fc5661a4035c51cd237d4b56647
BLAKE2b-256 checksum
How to use checksums
451e9e76c3473ef215e524f91c86f0a6f46f7f06f765b464f28a2b7d200688c7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / onebudgetspec_cli-0.1.1-py3-none-macosx_11_0_arm64.whl

Download URL onebudgetspec_cli-0.1.1-py3-none-macosx_11_0_arm64.whl
Size 1.4 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
6203896e07807cd21ae361dd96363841ae9b57d07354439e40b4d8bc0d8b38fb
BLAKE2b-256 checksum
How to use checksums
d46f0477c0ef782074b7e18ddf1e2f5a3e1c1ed47d06d6526cacb636a363e092
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / onebudgetspec_cli-0.1.1-py3-none-macosx_10_12_x86_64.whl

Download URL onebudgetspec_cli-0.1.1-py3-none-macosx_10_12_x86_64.whl
Size 1.5 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
217e1708f79fc6d668d62e21c1927330cae0d7fe141d6d8b89afba27e6d670cc
BLAKE2b-256 checksum
How to use checksums
2d6d0eed00533596064d6bdf94cf193976e1b8a2bf1195ff36e35c8e24ead1ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.5

6 release files

0.1.4

6 release files

0.1.3

6 release files

0.1.2

6 release files

This release

0.1.1 This release

4 release files

0.1.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page