/** * Rate cap-CSV parser — reads the engine-only safety-floor file that * `log10x_configure_engine` writes to the customer gitops repo. * * THIS PARSER IS FOR THE RATE CAP CSV ONLY: * File: `pipelines/run/receive/rate/caps.csv` * Header: `container,cap` * Format: numeric bytes-per-window, container-keyed (with optional * `pat:` per-pattern overrides) * * For the compact CSV (`pipelines/run/receive/compact/compact-cap.csv`), * which uses a boolean per-pattern format, use `compact-csv-parser.ts`. * * ENGINE GRAMMAR (rate-object-cap.js): `[:][:]` * — the second field is an EXPIRY EPOCH. The engine reads no action from * this file; the per-service action lives in the sibling `actions.csv` * (rateReceiverActionLookupFile), and per-pattern intent in * `data/action-intent.json`. A cap of 0 is a per-container regulator * opt-out engine-side, so written rows always carry a cap >= 1. * * Row format (set by configure-engine.ts): * container,cap ← header * payment-service,2048::MCP default ← per-service cap, no expiry * * This parser additionally tolerates two legacy MCP emissions so old repos * round-trip: * - `::` — a folded action in the epoch slot * (which the engine never read as an action) * - `::[:]` — a trailing action suffix * In either shape a recognized action token is stripped from the reason and * surfaced on `legacy_action_suffix` — diagnostics only, never routing. * * Two row shapes: * - `pat:` rows — per-pattern overrides; the `key` field of * CapCsvRow is the bare `` (the `pat:` prefix is stripped). * - `` rows — container-level defaults; the `key` field is * the container name and `isContainerDefault=true`. */ import type { Action } from './cost.js'; export interface CapCsvRow { /** * For `pat:` rows this is the bare pattern_hash (`pat:` prefix * stripped). For container rows this is the container name. */ key: string; /** True when the row's CSV key did NOT start with `pat:`. */ isContainerDefault: boolean; /** Parsed cap in bytes per 4-minute reset window. NaN-safe (clamped to 0). */ bytes_cap: number; /** Free-text reason label (commas already substituted to `;` upstream). */ reason: string; /** * The action token parsed out of the row's value, under either grammar. * Held for diagnostics and attribution; the engine reads the action from * the cap row itself. For the action-intent.json path, the canonical plan * is in `data/action-intent.json`. */ legacy_action_suffix?: Action; } export interface ParseCapCsvResult { rows: CapCsvRow[]; /** * Pattern-hash → CapCsvRow lookup, populated only for `pat:` rows. * Container-default rows are NOT inserted here — callers that want a * per-pattern → bytes_cap mapping with container fallback should use * `by_container` together with the pattern's container label. * * NOTE: for the action-intent.json path, read `data/action-intent.json` * via `fetchAndParseActionIntent` in `action-intent-parser.ts` rather * than taking action attribution from this lookup. */ by_pattern: Map; /** * Container → CapCsvRow lookup for the container-default rows. Used * to resolve per-container byte caps when a pattern has no explicit * `pat:` override row. */ by_container: Map; /** * Lines that could not be parsed (preserved verbatim for caveat * surfacing). Empty when the CSV is well-formed. */ malformed_lines: string[]; } /** * Parse a cap-CSV string into rows + lookups. * * Tolerates: * - missing/extra header lines (only the `,` shape is required) * - missing `::` separator (treats whole value as bytes, action='drop', * suffix_missing=true) — surfaced to callers via the per-row flag * - blank lines and CRLF endings * * Does NOT mutate caller-owned strings. Returns an empty result when * `content` is undefined/empty/whitespace. */ export declare function parseCapCsv(content?: string | null): ParseCapCsvResult; /** * Build a pattern_hash → Action lookup with container-default fallback. * * Reads the action token off the cap rows themselves. Callers on the * action-intent.json path should use `fetchAndParseActionIntent` from * `action-intent-parser.ts` for the canonical pattern → action map * instead. * * Resolution order: * 2. The pattern's container default row `legacy_action_suffix` → that value * 3. Absent from the map — callers treat absence as "unattributed" * * The caller knows which container a pattern_hash belongs to via the * TSDB query (the engine emits both `tenx_hash` and `k8s_container` as * labels on `all_events_summaryBytes_total`). */ export declare function buildPatternActionLookup(parsed: ParseCapCsvResult, patternToContainer: Map): Map; /** * Build a pattern_hash → bytes_cap lookup with container-default fallback. * * This is the primary non-deprecated function for reading cap CSV data. * It returns the bytes cap for each pattern, which is the engine safety * floor, not the action intent (action intent is in action-intent.json). * * Resolution order: * 1. `pat:` row → that row's bytes_cap * 2. The pattern's container default row → container bytes_cap * 3. Absent from the map — pattern has no known cap */ export declare function buildPatternBytesCapLookup(parsed: ParseCapCsvResult, patternToContainer: Map): Map; /** * Action bucket totals — the canonical shape consumed by VerifyResult's * `per_action_breakdown`. The `unattributed` field holds bytes for * pattern_hashes that had routeState="drop" volume but no matching * cap-CSV row (neither `pat:` nor a container default). Surfaced * separately so the parts-≤-whole guard can subtract it before clamping. */ export interface ActionBytesBuckets { pass: number; sample: number; compact: number; tier_down: number; offload: number; drop: number; unattributed: number; } export declare function emptyActionBuckets(): ActionBytesBuckets; /** * Sum bucket values into a single bytes total. Excludes `unattributed` * by default (it's already part of the whole; the breakdown is what the * MCP could attribute). */ export declare function totalAttributedBytes(buckets: ActionBytesBuckets): number;