VERIFISDK docsengine v2 · contract 1.5.1 · harness-sdk 1.2.0
Harness authoring

The budget ledger

ctx.budget meters and alerts. It cannot refuse a spend, and it must never grow the ability to.


budget: BudgetLedger;

Source: verifi-harness-sdk/src/budget-ledger.ts, with the schema in @pragyacyber/engine-contract's budget.ts.

Where spend enters, and what happens when it piles up

WHERE SPEND ENTERS — THE ONLY THREE PLACES ctx.mcp().call() ok toolCalls +1 · evidenceBytes += len(text) ctx.mcp().call() transport fail toolCalls +1 — a failed call still cost you ctx.model().invoke() ok tokens += in+out · costUsd from price table BudgetLedger costUsd tokens toolCalls evidenceBytes subAgents elapsedMinutes record() always succeeds — that is the point threshold crossed threshold_breach severity warn · each level once It alerts. It does not stop anything. THE ONE HARD BOUND budget.maxDurationMinutes read via timeRemaining() / pastDeadline() time is the only ceiling in the system WHAT DOES NOT EXIST, BY DESIGN budget.canProceed() · enforce() · maxCostUsd the schema is strict, so a resurrected cap is a parse error rather than a regression
The ledger meters; it cannot refuse. Every axis except elapsed time is advisory — crossing a threshold produces an event a human reacts to, not a halt. That is a deliberate inversion of the usual design: a scanner stopped mid-sweep by a cost cap produces a partial result that looks like a complete one, which is the exact failure this platform is built to avoid. Time is the only ceiling because a run that never ends is the one failure alerting cannot fix.

The one rule

The reasoning, verbatim from the source:

Capping cost, tokens, tool calls or evidence severs a scan mid-test. The target is left half-examined, the findings are partial, and the delivered report is wrong in a way that is hard to detect from the outside. An overspend is a bill; a truncated assessment is a defective product shipped to a client.

What remains is metering. And because metering is now the entire control surface, the metered path being the only way to spend matters more than it did when caps existed — a bypass is no longer an overspend risk, it is a run nobody can see.

The budget

export const zBudget = z.object({
  /** HARD. Breach ⇒ Run → 'timed_out'. Bounds cost by rate × time. */
  maxDurationMinutes: z.number().int().positive(),

  // crossing these NEVER stops anything
  costUsdThresholds: zLevels,
  tokenThresholds: zLevels,
  toolCallThresholds: zLevels,
  evidenceByteThresholds: zLevels,

  /** Advisory. A harness may consult it; nothing enforces it. */
  maxSubAgents: z.number().int().positive(),

  /** Ledger flush + threshold-evaluation cadence. */
  checkpointEverySeconds: z.number().int().positive().default(30),
}).strict();

zLevels is an array of positive numbers that must be strictly ascending, so "worse" is unambiguous. A typical shape is [expected, 2×, 5×].

The schema is .strict() specifically so that a resurrected maxCostUsd or policy: 'hard' | 'soft' is a loud parse error rather than a silently ignored field somebody believes is being enforced.

Reading it

export class BudgetLedger {
  record(delta: Partial<Omit<Spend, 'elapsedMinutes'>>): void;
  spent(): Spend;
  elapsedMinutes(): number;
  /** Minutes until the hard deadline. The only hard bound in the system. */
  timeRemaining(): number;
  /** True once the deadline has passed. */
  pastDeadline(): boolean;
  /** Advisory. Nothing enforces it. */
  overThreshold(): ThresholdBreach[];
  /** An accounting split for a sub-agent. Not a cap. */
  allocate(): BudgetLedger;
}
export interface Spend {
  costUsd: number;
  tokens: number;
  toolCalls: number;
  evidenceBytes: number;
  elapsedMinutes: number;
  subAgents: number;
}

You will rarely call record() — the MCP client and the model client do it for you on every call. Call it yourself only for spend the SDK cannot see.

Making cheaper choices

You may consult the ledger. Nothing requires you to.

// Shrink the sweep when we are past the first cost threshold.
const breaches = ctx.budget.overThreshold();
const depth = breaches.some((b) => b.metric === 'cost_usd') ? 'fast' : 'thorough';

// Stop starting new work when there is not time to finish it.
if (ctx.budget.timeRemaining() < 5) {
  ctx.log('under five minutes remaining — skipping the deep phase');
  return finish();
}

timeRemaining() and pastDeadline() are the only two that describe an actual bound. Everything else is advisory.

Thresholds fire once

export function crossedThresholds(
  levels: readonly number[],
  actual: number,
  alreadyFired: readonly number[],
): number[];

Each level fires at most once per run. An alert that repeats on every call is noise, and noise is precisely how the platform's only control surface stops being read.

Verified: crossedThresholds([1, 5, 25], 6, [1]) returns [5] — the already-fired 1 is excluded, and 25 has not been reached.

A crossing produces a ThresholdBreach:

{ metric: 'cost_usd' | 'tokens' | 'tool_calls' | 'evidence_bytes',
  threshold: number, actual: number, alertedAt: string }

runHarness wires the ledger's onBreach to emit a threshold_breach event at warn. That alert is the whole mechanism. It does not stop anything.

Sub-agents

const sub = ctx.budget.allocate();

Returns a new BudgetLedger sharing the same budget, events and clock. It is an accounting split, not a cap: exceeding it changes nothing, and a sub-agent that runs past its share keeps working. Note the returned ledger starts its own elapsed clock and its own fired-threshold set, so a sub-agent can re-fire a level the parent already fired.

What the engine sends down

The gRPC channel carries a BudgetUpdate down-message with reconciled spend, breach state and minutes remaining. GrpcEngineChannel ignores it, deliberately:

// budgetUpdate is deliberately ignored here: it is ADVISORY. A harness that wants
// to be frugal reads ctx.budget; nothing about it can halt a run.

There is currently no way for a harness to observe the engine's reconciled view of spend. Your ctx.budget reflects only what this container metered.