import type { UsageProfile } from './types.js'; /** * The cost baseline: what this repository's prompts cost as of a commit, and * what has happened to them since. * * **Why this exists.** `budgets` in `trazum.config.json` is a ceiling — it * answers "does this file fit". It cannot answer "did this change make things * worse", and those are different questions with different failure modes. A * repository sitting at 95% of every budget passes every gate forever while a * pull request quietly adds four hundred tokens across a dozen files. A ceiling * catches the absolute; only a baseline catches the drift. * * **Why the gate is in tokens and the money is only reported.** A dollar figure * is derived from three things: the token count, the usage scenario, and the * price list. Two of those change for reasons that have nothing to do with the * prompts — a repriced model, an edited `callsPerMonth` — so a baseline holding * dollars would fail a build the day the catalogue was updated, calling a price * change a regression. A gate that cries wolf is a gate somebody deletes. * * Tokens depend on the text and nothing else. They are what is compared, they * are what the threshold is written in, and the monthly figure is recomputed at * comparison time and shown next to it — with an explicit note when the scenario * or the price list moved, because a dollar delta across a reprice is two * different measurements subtracted from each other. * * No filesystem access here, deliberately: `apps/web` bundles this package for * the browser, and one `node:fs` import anywhere in the graph fails that build. * Reading and writing the file is the CLI's job. */ /** * The document version. * * Written into every baseline and checked on read. A baseline is committed and * outlives the version of Trazum that wrote it, so a future shape change has to * be able to say "this file is from an older format, re-record it" rather than * misreading fields that moved. */ export declare const BASELINE_VERSION = 1; export declare const BASELINE_FILENAME = "trazum.baseline.json"; /** * Largest baseline this will read. * * Bigger than the config limit because this one scales with the repository: a * thousand prompts is a thousand entries. Still bounded, so a corrupt or hostile * file is refused before `JSON.parse` is handed the whole thing. */ export declare const MAX_BASELINE_BYTES: number; export interface BaselineDocument { version: number; /** ISO date the baseline was recorded, for the report to cite. */ recorded: string; /** * The scenario the monthly figure was computed under. Recorded so a later * comparison can say whether the money is comparable, not to gate on. */ scenario: UsageProfile; /** `PRICING_LAST_REVIEWED` at the time, for the same reason. */ pricingReviewed: string; totals: { tokens: number; monthlyUsd: number; }; /** * Repository-relative path to token count, forward slashes always. * * Per file rather than one total, because a gate that reports "the repository * grew by 400 tokens" without naming the file is a gate people learn to * ignore. Sorted on write so re-recording produces a reviewable diff instead * of a reordered one. */ files: Record; } export declare class BaselineError extends Error { readonly source: string; constructor(message: string, source: string); } /** * Validates a baseline document. * * **Every failure throws**, for the reason the config parser gives: this file * decides whether a build passes. A lenient read of a malformed baseline is a * gate that measured nothing and reported success, which is worse than no gate, * because the repository now believes it has one. */ export declare function parseBaseline(raw: string, source?: string): BaselineDocument; /** * The document as text, ready to commit. * * Keys are emitted in a fixed order and file paths sorted, so re-recording an * unchanged repository produces a byte-identical file. A baseline that reshuffles * itself on every write turns every pull request into an unreviewable diff, and * the first thing anyone does with an unreviewable diff is stop reading it. */ export declare function formatBaseline(document: BaselineDocument): string; export interface BaselineChange { path: string; before: number; after: number; /** `after - before`, so positive is growth — the direction that costs money. */ delta: number; } export interface BaselineComparison { grown: BaselineChange[]; shrunk: BaselineChange[]; /** * Present now, absent from the baseline. * * Counted toward the total, which is the whole reason this field exists: a new * prompt is new cost, and a comparison over only the files present in both * would let a five-thousand-token addition through every threshold. */ added: BaselineChange[]; /** In the baseline, gone from the tree. Never a regression — it is a saving. */ removed: BaselineChange[]; tokensBefore: number; tokensAfter: number; delta: number; /** Growth as a percentage of the baseline, or 0 when the baseline was empty. */ deltaPct: number; } /** * Compares a set of current token counts against a baseline. * * Pure arithmetic over two maps. It takes counts rather than file contents so * the caller decides what a prompt is — directory mode, extracted markers, a * hand-picked list — and this stays the one place the comparison is defined. */ export declare function compareToBaseline(baseline: BaselineDocument, current: Record): BaselineComparison; export interface BaselineThresholds { /** Absolute token growth allowed. */ maxGrowthTokens?: number; /** Growth allowed as a percentage of the baseline total. */ maxGrowthPct?: number; } /** Why a comparison failed. Structured, so the CLI owns the wording. */ export type BaselineBreach = { kind: 'tokens'; limit: number; actual: number; } | { kind: 'pct'; limit: number; actual: number; }; /** * Whether a comparison breaches its thresholds. * * Both thresholds are checked and **either one failing fails the gate** — they * are not alternatives to pick between. A percentage alone lets a small * repository absorb a large absolute addition; an absolute number alone means a * large repository never trips. Whichever is exceeded is reported, so the output * names the limit that was actually crossed rather than a generic failure. * * Shrinking never fails. There is no such thing as a prompt that got too cheap. */ export declare function breaches(comparison: BaselineComparison, thresholds: BaselineThresholds): BaselineBreach[]; /** * Whether the baseline's money is comparable to today's. * * Tokens are always comparable — they depend on the text and nothing else, which * is why the gate is written in them. The monthly figure is not: a repriced model * or an edited scenario changes it without a single prompt moving. When either * has shifted the report says so instead of subtracting two different * measurements and presenting the difference as a saving. */ export declare function moneyIsComparable(baseline: BaselineDocument, scenario: UsageProfile, pricingReviewed: string): { comparable: boolean; scenarioChanged: boolean; pricingChanged: boolean; }; //# sourceMappingURL=baseline.d.ts.map