/** * Whose budget — the question that decides whether anything on the list gets * done. * * The fleet answered *which service* in 1.37. Nobody has answered *whose * money*, and until somebody does, every finding this product makes lands on a * desk with no name on it. A report that says "the bill is $40,000 and here is * how to save $9,000" is read by four people who each assume it is one of the * other three's problem. * * ## The unallocated is its own line, and it is never spread * * The one rule worth breaking the module over. * * Splitting unattributed spend proportionally across the owners you *do* know * is the single most common lie in cost reporting. It is attractive because it * makes the numbers add up and every line look complete. What it actually does * is make **every team's figure wrong**, by an amount nobody can see, in a * direction nobody can check — and it does it most to the teams with the * cleanest instrumentation, because they are the ones whose known spend is * largest and who therefore absorb the biggest share of somebody else's * mystery. * * So the unallocated stays a line of its own, with its own dollar figure, until * a human claims it. It is loud on purpose: an unallocated share that grows * quietly is a chargeback report becoming fiction one month at a time. * * ## Shared cost is declared, never inferred * * A workload two teams use is split by a rule somebody wrote down, and **the * rule travels with the report**. That is the whole design: the argument then * happens about the rule — "why is search 60/40?" — rather than about the * number, which is an argument nobody can win because nobody can see where the * number came from. * * A split that does not sum to one is a configuration error and not a rounding * problem, because the alternative is silently losing or inventing money. * * ## An owner with no measured data is not an owner under budget * * `fleetBudgetMissing`, from 1.37, applied to people. A team whose logs never * arrived passes every budget it has, forever, and a report that renders that * as a green tick has told somebody the opposite of the truth. */ /** How one workload's spend is divided between owners. Sums to 1. */ export type SharedSplit = Record; export interface OwnersConfig { /** Label patterns per owner. Most specific wins, as everywhere in this tool. */ patterns: Record; /** * Workloads two or more owners share, split by a rule a human wrote. * * Keyed by the exact label rather than by a pattern: a shared split is a * negotiated fact about one workload, and letting it match a glob would mean * a new label silently joining somebody's bill. */ shared?: Record; /** Monthly budgets per owner, in dollars. */ budgets?: Record; } export type OwnerProblem = { kind: 'split-does-not-sum'; label: string; total: number; } | { kind: 'split-names-unknown-owner'; label: string; owner: string; } | { kind: 'split-has-one-owner'; label: string; owner: string; } | { kind: 'budget-for-unknown-owner'; owner: string; } | { kind: 'negative-share'; label: string; owner: string; share: number; }; /** * Everything wrong with an ownership config, before any money is attributed. * * Returned rather than thrown, so all of it can be reported at once. A * chargeback config fixed one error per run is a chargeback config somebody * abandons halfway and then never trusts. */ export declare function validateOwners(config: OwnersConfig): OwnerProblem[]; /** One workload's spend, as the caller measured it. */ export interface LabelSpend { label: string; usd: number; calls: number; } export type OwnerVerdict = 'within' | 'over' | 'not-measured' | 'no-budget'; export interface OwnerLine { owner: string; usd: number; calls: number; /** Which labels landed here, and how — so the attribution is checkable. */ from: Array<{ label: string; usd: number; via: 'pattern' | 'shared'; share?: number; }>; budgetUsd: number | null; verdict: OwnerVerdict; } export interface Allocation { owners: OwnerLine[]; /** * Spend that matched no owner — its own line, never spread. * * `labels` names them, because "unallocated: $4,300" invites somebody to * divide it and "unallocated: $4,300 across `search-v2` and `internal-eval`" * invites somebody to claim it. */ unallocated: { usd: number; calls: number; labels: string[]; }; /** The shared rules that were applied, carried so the report can print them. */ sharedApplied: Array<{ label: string; split: SharedSplit; }>; problems: OwnerProblem[]; } export declare function allocate(spend: readonly LabelSpend[], config: OwnersConfig): Allocation; //# sourceMappingURL=owners.d.ts.map