cyberine
UsageRun recipes

Task footprint estimator — history-driven budget planning (kx9rt, calibrated in k5m89)

planBudget() decides whether a step can spawn all its workers, must chunk into a handoff chain, should degrade, or has to abort. Its cost side comes from the credit ledger above; its SIZE side comes from estimateFootprint(), which reads past runs out of the runs directory and takes a percentile of what tasks like this one actually consumed on the worker in question.

Two numbers come back, and they answer different questions:

FieldStatisticUse it for
estInputTokens / estOutputTokens / estCostUsdmedian of matching samplesreporting the TYPICAL case to a human
upperBoundTokens / upperBoundCostUsdp90 of matching samplesthe admission decision (what a worker must have left)
basisworker | kind | heuristicwhich key produced the estimate
confidence1 - exp(-n/10)how many samples are behind it (display only — nothing gates on it)

basis is the field to read when a number looks surprising. worker means the estimate came from that worker's own history for that kind and can legitimately sit orders of magnitude away from a sibling's; kind means the worker has fewer than 3 samples and is carrying the blended number; heuristic means there is no history at all.

Nothing to configure — it reads whatever run history is already on disk:

# The runs directory the corpus reads. Same resolution order as RunStore:
#   CYBERINE_RUNS_DIR  ->  <cwd>/.local/runs
CYBERINE_RUNS_DIR=/path/to/runs cyberine route "refactor the parser"

# Guard: corpus is populated from real samples AND honours CYBERINE_RUNS_DIR
npm run selftest:footprint-corpus

With no history the estimate is a fixed low-confidence fallback (3000 in / 1500 out, confidence 0.2, basis heuristic). With history it reports numbers drawn from your own runs.

What the calibration pass found (k5m89, measured against this repo's own 244 runs). The estimator went live on 2026-08-23 after years of silently returning the fallback (see below), and the first numbers it produced were not usable. Two things were wrong, both fixed:

  • It reported the mean. These distributions are heavy-tailed by nature — one whole-repo-context run costs a thousand times a one-file edit — so the mean chased the tail and described no run that had ever happened: code-large median 17.4k total tokens against a mean of 441.8k (25x), code-small median 2.3k against a mean of 124.3k (54x). The typical-case fields are now the median; the p90 upper bound is unchanged and is still what the gate reserves.
  • It was keyed by task kind alone. Within the single kind code-large, opencode-deepseek's median run was 309 tokens while loop-mimo-build's was 532.8k — a ~1700x spread that one blended number cannot represent in either direction, and it was wrong in BOTH at once: the admission bound over-reserved 1722x for the cheap CLI workers (334k tokens demanded of a worker whose p90 is 194) while under-reserving for the heavy loop workers (1150k demanded of loop-deepseek-build, whose own p90 is 2714k — so the gate admitted it on less than half of what it actually needs). planBudget() now estimates per worker. Once keyed that way the mean/median ratio collapses from 25x to 1.1-8.3x, which is the evidence that the heavy tail was almost entirely BETWEEN workers, not within them.

A third inconsistency went with them: the USD admission path compared the MEAN cost against the balance while the token path compared p90, making the USD gate materially laxer for the same task. Both now compare a p90 (upperBoundCostUsd / upperBoundTokens).

Known limitations and deferred work. basis: 'kind' still means a blended estimate — a worker under 3 samples (MIN_PAIR_SAMPLES) gets the fleet-wide number, so a brand-new worker's first runs are gated on other workers' history. Nothing is keyed by model or repo size, so the same worker on a 200-file repo and a 3-file repo shares one estimate. The bound is a p90 by nearest-rank, which means it deliberately does NOT cover the top decile: at n=10 the single worst sample sits outside the bound by construction, so roughly 1 task in 10 is expected to exceed its reservation — that is the difference between p90 and max, and raising it is a risk-posture change, not a bug fix. The corpus is loaded once per process and cached, so a run finishing mid-process does not widen it until the next invocation (tests force a reload with resetFootprintCorpus()). Runs with no recorded usage are skipped rather than counted as zero. agentId is keyed on the part before @, so a swarm chunk (c-01-add-helper@opencode) or queue slot (loop-deepseek-direct@q0) folds into its base worker. And because the numbers come from local history, two machines with different run histories can legitimately reach different spawn/chunk decisions for the same task; anything asserting on that decision must pin CYBERINE_RUNS_DIR to a fixture.

Until 2026-08-23 this whole estimator was dead: loadCorpus() reached node:fs through a lazy require(), which is not defined in this ESM package, and a bare catch swallowed the throw — so every estimate silently returned the fallback. npm run selftest:footprint-corpus is the regression guard, and it now pins the median, the per-worker keying, the @-suffix folding, and both p90 bounds — each positive-controlled by reverting the fix and watching the smoke go red.