/** * log10x_commitment_report — promised-vs-delivered tracking for x%-by-Y * commitments. * * Customer signs a commitment ("we will deliver X% spend reduction on * service S against destination D by date Y") via `log10x_configure_engine`. * That tool persists a commitment record to the snapshot-store namespace * `commitments/`. This tool, called periodically (weekly is the design * cadence), reports promised vs delivered, attributes variance, and * projects forward Bayesian confidence. * * Contract-awareness — IMPORTANT for Datadog & similar committed-volume * deals where the contract ratchets up but not down: * * YEAR-ONE (`contract_type='committed'`): * - We CAN'T reduce dollar spend (volume is already paid for the * remainder of the term), but we CAN bank bytes-saved as headroom * against future overages and as evidence at the next renewal. * - delivered_dollars is reported as a SHADOW number with caveat. * - delivered_bytes is the primary KPI. * * YEAR-TWO ONWARD (`contract_type='on_demand'`, or `contract_type='committed'` * past `term_end`): * - Bytes-saved translates to dollar-saved at the contract unit rate. * - delivered_dollars becomes the primary KPI. * * Bayesian forward confidence: Beta(α,β) prior, weak by default (2,2), * updated with weekly fractional evidence. Posterior CDF gives p10 / p90 * for next-90d. See spec §5 default-chosen Q4. * * Dependencies (some not yet shipped in this branch; references stubbed * with explicit `not_ready` envelopes when missing): * - estimate-savings.ts → `runEstimateVerify` (one weekly window) * - configure-engine.ts → writes commitment records to snapshot-store */ import { z } from 'zod'; import { type StructuredOutput } from '../lib/output-types.js'; import { type CustomerMetricsBackend } from '../lib/customer-metrics.js'; import { type Action, type DisclosedDollarValue } from '../lib/cost.js'; import { type SiemId } from '../lib/siem/pricing.js'; export declare const commitmentReportSchema: { commitment_id: z.ZodOptional; service: z.ZodOptional; period: z.ZodDefault>; format: z.ZodDefault>; history_path: z.ZodOptional; action_intent_path: z.ZodOptional; environment: z.ZodOptional; }; declare const schemaObj: z.ZodObject<{ commitment_id: z.ZodOptional; service: z.ZodOptional; period: z.ZodDefault>; format: z.ZodDefault>; history_path: z.ZodOptional; action_intent_path: z.ZodOptional; environment: z.ZodOptional; }, "strip", z.ZodTypeAny, { format: "json" | "summary" | "cfo_md" | "weekly_digest"; period: "30d" | "90d" | "ytd"; service?: string | undefined; environment?: string | undefined; history_path?: string | undefined; commitment_id?: string | undefined; action_intent_path?: string | undefined; }, { service?: string | undefined; format?: "json" | "summary" | "cfo_md" | "weekly_digest" | undefined; period?: "30d" | "90d" | "ytd" | undefined; environment?: string | undefined; history_path?: string | undefined; commitment_id?: string | undefined; action_intent_path?: string | undefined; }>; export type CommitmentReportArgs = z.infer; /** * Commitment record persisted by `log10x_configure_engine` on PR merge. * * `contract_type='committed'` means a committed-volume contract (Datadog * Enterprise, Splunk Enterprise commit) — year-one dollar savings are * theoretical, year-two onward they're realized. `contract_type='on_demand'` * means usage-billed (CloudWatch, ES Service, etc.) — dollar savings * realize immediately. */ export interface CommitmentRecord { id: string; env: string; service: string; destination: SiemId; promised_pct: number; contract_type: 'committed' | 'on_demand'; /** ISO-8601 start of the commitment window (when the engine config went live). */ started_at: string; /** Baseline window the promised_pct was measured against (e.g. '30d'). */ baseline_window: string; baseline_bytes_30d: number; baseline_usd_monthly: number; /** * For contract_type='committed', the contract term-end date. * After this date, dollar savings switch from shadow to realized. */ term_end?: string; /** * Where configure_engine wrote the policy. Lets the verify runner * find the cap-CSV + action-intent.json regardless of delivery channel. * - 'gitops' — fetch via `gh api /repos//contents/` * - 'configmap' — fetch via `kubectl get configmap -n ` * Absent on records persisted before this field was added; verify * falls back to env.gitops?.repo for those. */ delivery_target?: { kind: 'gitops'; repo?: string; } | { kind: 'configmap'; namespace: string; name: string; }; } /** Persist a commitment record. Called by configure-engine.ts on PR-merge. */ export declare function putCommitment(rec: CommitmentRecord): void; /** Load a commitment by id. Returns undefined when missing. */ export declare function getCommitment(id: string): CommitmentRecord | undefined; /** * Newest configmap delivery target across ALL commitments. Doctor uses it * to find the policy ConfigMap the MCP actually wrote to, instead of * guessing from the MCP process's own env (the receiver's env, not ours). */ export declare function findNewestConfigMapTarget(): { namespace: string; name: string; } | undefined; /** Most-recent commitment for a service (used when commitment_id omitted). */ export declare function findCommitmentByService(service: string, env?: string): CommitmentRecord | undefined; /** * Shape of one weekly verify-mode output from estimate-savings.ts. * Defined here so commitment-report can compile and run before * estimate-savings ships. When the real `runEstimateVerify` lands, * it must produce this shape (or be adapted via `_setVerifyRunner`). * * Attribution fields are NORMALIZED FRACTIONS in [0,1]. */ export interface WeeklyVerifyResult { week_start: string; bytes_in: number; bytes_dropped: number; delivered_pct: number; delivered_dollars: number; attribution: { cap_fired: number; drift: number; new_patterns: number; leakage: number; }; at_risk_patterns?: Array<{ pattern_hash: string; issue: 'leakage' | 'new_pattern_uncapped' | 'drift'; suggested: string; }>; /** * Source of the $/GB rate used to compute `delivered_dollars` for the * week. Propagated from estimate-savings.ts verify-mode output. * - 'customer_supplied' — caller passed effective_ingest_per_gb * - 'list_price' — from vendors.json defaults * - 'unset' — no rate; `delivered_dollars` is null */ rate_source?: 'list_price' | 'customer_supplied' | 'unset'; /** * Per-pattern breakdown sourced from the cap CSV `:::` * row format (see project_unified_savings_tool.md). When the engine * regulator JS does not parse the `:` suffix yet, this field * is absent — the commitment report falls back to attributing all * `bytes_dropped` to the `drop` bucket (legacy behaviour) and pushes a * caveat noting the breakdown is unavailable. * * When `action_taken` is omitted on a row, the report defaults it to * `'pass'` and contributes 0 bytes to every bucket — preserves the * §A.1 invariant (drop+compact+offload+tier_down ≈ delivered_pct) * without double-counting unmarked patterns. */ per_pattern_breakdown?: Array<{ pattern_hash: string; /** Human pattern name; renderers lead with this, never the hash. */ pattern?: string; action_taken?: Action; bytes_saved: number; dollars_saved?: number | null; rate_source?: 'list_price' | 'customer_supplied' | 'unset'; }>; } /** * Hook called once per ISO week in the report period. The real * `runEstimateVerify` lives in estimate-savings.ts (separate chat). * This indirection lets the report run with a stub in tests AND lets * the integrating chat point at the live implementation by calling * `_setVerifyRunner` at module load. */ export type VerifyRunner = (args: { backend: CustomerMetricsBackend; commitment: CommitmentRecord; week_start: string; week_end: string; }) => Promise; /** * Wire the live runEstimateVerify (estimate-savings.ts calls this at * module-load when it ships). Until then, the commitment report * surfaces a clear `not_ready` envelope explaining the missing dep. */ export declare function _setVerifyRunner(impl: VerifyRunner): void; /** Test hook — clear the wired runner. */ export declare function _clearVerifyRunner(): void; /** Test hook — read the current runner (undefined when unwired). */ export declare function _getVerifyRunner(): VerifyRunner | undefined; /** * Minimal shape consumed from `runEstimateVerify` — typed locally so the * adapter doesn't pull in the full `VerifyResult` import (keeps the * runtime wire-up in index.ts as the only place that touches both * modules together; avoids any future circular-import risk between * `tools/commitment-report` and `tools/estimate-savings`). * * Fields kept narrow on purpose: a later change will join the * cap-CSV `:` suffix to fill `per_pattern_breakdown`. Until * then, this adapter leaves the per-pattern field unset and the * aggregator's single-bucket fallback bucketizes everything into `drop` (with * the caveat already wired at commitment-report.ts:1182). */ export interface VerifyResultLike { destination: SiemId; /** Fraction in [-∞, 1] from estimate-savings: 1 - postPassed/scaledBaseline. */ delivered_pct: number; post_passed_bytes: number; post_dropped_bytes: number; delivered_dollars_now: number; attribution_pct: { cap_fired_bytes: number; drift_bytes: number; new_patterns_bytes: number; leakage_bytes: number; }; /** Source of the $/GB rate used inside runEstimateVerify. a later change will * propagate this from estimate-savings; today the function chooses the * list price unless the caller passes effective_ingest_per_gb, so the * adapter encodes that choice with the right rate_source label. */ rate_source?: 'list_price' | 'customer_supplied' | 'unset'; /** * Per-pattern action attribution from runEstimateVerify. When present, * the adapter populates WeeklyVerifyResult.per_pattern_breakdown so the * commitment-report aggregator buckets bytes by engine action. Actions * are sourced from `data/action-intent.json` (canonical) with legacy * cap-CSV suffix as fallback. Absent when neither `action_intent_content` * nor `cap_csv_content` was supplied to verify. */ per_pattern_breakdown?: Array<{ pattern_hash: string; /** Human pattern name (TSDB pattern label); falls back to the hash. */ pattern?: string; action: Action; delivered_bytes: number; expected_bytes: number | null; action_source: 'pat_row' | 'container' | 'unattributed'; }>; } /** * Adapter: `VerifyResult` (estimate-savings.ts) → `WeeklyVerifyResult` * (this file). Item-1 narrow scope: * - delivered_pct: VerifyResult.delivered_pct is a fraction (1 - x); * WeeklyVerifyResult is a percentage in [0, 100], clamped. * - bytes_in / bytes_dropped: from VerifyResult post-window totals. * Treating `post_passed + post_dropped` as the week's bytes_in is * correct when the post window IS the week being reported. * - attribution: VerifyResult.attribution_pct fields are already * fractions in [0,1] (estimate-savings.ts:775 pctOf), matching * WeeklyVerifyResult.attribution's normalized-fraction contract. * - per_pattern_breakdown: populated when runEstimateVerify received * `action_intent_content` (canonical, from data/action-intent.json) * or legacy `cap_csv_content` rows with `:action` suffixes. When * neither is available, the single-bucket fallback in `aggregateWeekly` * attributes all bytes_dropped to the `drop` bucket. * * `week_start` is propagated through verbatim so the weekly_series * row labels match the report's enumerated week-boundary cursor. */ export declare function adaptVerifyResultToWeekly(vr: VerifyResultLike, week_start: string): WeeklyVerifyResult; export interface CommitmentReportEnvelope { commitment: { id: string; service: string; destination: SiemId; promised_pct: number; contract_type: 'committed' | 'on_demand'; started_at: string; }; period: { start: string; end: string; days: number; }; delivered_pct: number; /** * Share of `bytes_in` saved by each engine action, 0..100 percent. * * Each share is a percent of bytes_in (NOT a share of delivered_pct). * Invariant: drop + compact + offload + tier_down + sample ≈ delivered_pct * within ±1pp rounding. `pass` always contributes 0 (the engine did not * touch the bytes) but is surfaced for completeness so a caller can * verify the bucket map covers every action label the per_pattern_rows * carries — i.e. the bucket map is structurally exhaustive over the * action enum, not the narrow "savings actions only" subset. * * On reconciliation failure (bucket sum diverges from delivered_pct by * more than 1pp), a `reconciliation_warning` caveat is pushed; the * envelope is rendered without modification so the CFO can see the * mismatch instead of getting a silently-corrected number. * * The `offload` bucket is the metric-side `dropped_bytes_in_window` * for patterns where `getOffloadStatusBatch` returned `is_offloaded`; * the metric stamp overrides any action-intent entry. On metric backend * timeout the bucket is 0 and a soft-warning lands in `caveats`. */ percent_reduction_by_action: { drop: number; compact: number; offload: number; tier_down: number; sample: number; pass: number; }; delivered_bytes: number; /** * Bytes saved by each engine action — bytes counterpart of * `percent_reduction_by_action`. Sum to `delivered_bytes` by * construction when the offload helper succeeds. On offload-helper * timeout the offload bucket is 0 and a caveat surfaces that the * contribution was omitted. * * Same key set as `percent_reduction_by_action` — sample carries real * bytes_saved (sample N=2 ≈ 50% reduction per pattern), pass stays 0. */ bytes_saved_by_action: { drop: number; compact: number; offload: number; tier_down: number; sample: number; pass: number; }; /** * For year-one committed contracts: the THEORETICAL dollar value of * the bytes saved (banked, not realized). For on-demand contracts and * post-term-end committed contracts: realized dollar savings. * * Null when `rate_source === 'unset'` — no $/GB rate available, the * report leads with the byte/percent KPI and gates every dollar phrase. */ delivered_dollars: number | null; /** Disclosed-value mirror of delivered_dollars; null when rate_source==='unset'. */ delivered_dollars_disclosed: DisclosedDollarValue | null; delivered_dollars_kind: 'realized' | 'shadow_committed_year_one'; promised_dollars: number | null; /** Disclosed-value mirror of promised_dollars; null when rate_source==='unset'. */ promised_dollars_disclosed: DisclosedDollarValue | null; /** * Aggregate rate source across the weekly slices. Reduced from each * week's `rate_source`: * - all 'customer_supplied' → 'customer_supplied' * - any 'unset' or mixed/none → 'unset' * - otherwise → 'list_price' * Surfaced inline in the dollar paragraph and gates the list-price * disclaimer. */ rate_source: 'list_price' | 'customer_supplied' | 'unset'; variance_attribution: { cap_fired_pct: number; drift_pct: number; new_patterns_pct: number; leakage_pct: number; }; weekly_series: Array<{ week_start: string; delivered_pct: number; delivered_bytes: number; delivered_dollars: number | null; rate_source: 'list_price' | 'customer_supplied' | 'unset'; }>; forward_confidence: { p10_next_90d_pct: number; expected_next_90d_pct: number; p90_next_90d_pct: number; low_data_warning: boolean; }; at_risk_actions: Array<{ pattern_hash: string; issue: 'leakage' | 'new_pattern_uncapped' | 'drift'; recommended: string; /** * Service that emits the pattern (joined from TSDB after weekly * aggregation). Surfaced in the CFO markdown at-risk bullet so the * reader sees a service-anchored identity instead of the raw hash. * Undefined when the descriptor join failed for this hash. */ service?: string; /** * Engine `message_pattern` token string for the hash (joined from * TSDB). Rendered through `patternDescriptor` into the at-risk * bullet prose. Absent when the descriptor join returned no result. */ symbol_message?: string; }>; /** * Per-pattern attribution rows merged across the report period. * * `action_taken` is sourced from `data/action-intent.json` (canonical) * via the `runEstimateVerify` → `computeActionSplit` path, falling back * to legacy cap-CSV action suffixes for rows written before the * action-intent migration. Patterns flagged by * `getOffloadStatusBatch.is_offloaded` override to `'offload'` * regardless — the metric stamp is ground truth. `dollars_saved` is * null whenever the row's `rate_source === 'unset'`. * * Empty when the upstream verify runner did not return * `per_pattern_breakdown` for any week — see the caveat path in the single-bucket fallback. * * `intent_observation_mismatch` flags rows where the configured * intent (`action_taken='pass'`) disagrees with the observed * dropped-bytes signal (`bytes_saved > 0`). This is the canonical * policy-drift indicator: the engine is reducing bytes on a pattern * the policy says to pass through, OR the new policy has not yet * fully propagated to the cluster. The row is surfaced as-is — we * do NOT zero out bytes_saved here because the drift signal is * exactly what FinOps wants to see; rendering layers should call * out the flag rather than hide it. */ per_pattern_rows: Array<{ pattern_hash: string; /** Human pattern name; renderers lead with this, never the hash. */ pattern: string; action_taken: Action; bytes_saved: number; dollars_saved: number | null; rate_source: 'list_price' | 'customer_supplied' | 'unset'; intent_observation_mismatch?: boolean; }>; annualized_dollars: number | null; /** Disclosed-value mirror of annualized_dollars; null when rate_source==='unset'. */ annualized_dollars_disclosed: DisclosedDollarValue | null; caveats: string[]; markdown?: string; /** * One-paragraph plain-prose distillation. Populated on every success * path. Dollar figures gated by rate_source. */ human_summary?: string; } /** * Per-action attribution totals across the 7-day digest window. * Sourced from `data/action-intent.json` entries active during the period. */ export interface DigestActionSplit { /** Count of patterns actively assigned to this action. */ pattern_count: number; } /** * A single tick run as it appears in the digest tick-history section. */ export interface DigestTickEntry { /** ISO-8601 timestamp of the tick. */ ts: string; /** Tick outcome status. */ status: 'no_change' | 'applied' | 'dry_run' | 'error'; /** Projected savings percentage at tick time. */ projected_savings_pct: number; /** Number of patterns whose action changed vs. prior state. */ delta_patterns: number; /** Change in savings pp. */ delta_pp: number; /** Whether this tick wrote new CSV/intent files. */ changed: boolean; } /** * A pattern that was not in the prior week's action-intent but appeared * this week (new_this_week) or a pattern whose byte volume grew >5x * week-over-week (anomaly). */ export interface DigestPatternNote { pattern_hash: string; kind: 'new_this_week' | 'anomaly_growth'; /** * For anomaly_growth: ratio of current_week_savings_pct / prior_week_savings_pct. * Undefined for new_this_week. */ growth_ratio?: number; /** * Service that emits the pattern (joined from TSDB). Undefined when the * descriptor join failed or the action-intent entry had no `service` * field. Used as a fallback when `symbol_message` is unavailable. */ service?: string; /** * Engine `message_pattern` token string for the hash (joined from TSDB). * Source for the human-facing descriptor in the rendered prose. Absent * when the descriptor join returned no result for this hash. */ symbol_message?: string; /** Human-readable note. */ note: string; } /** * The envelope produced by `format=weekly_digest`. * * Reads the recurring-tick audit trail (JSONL) plus the current * `data/action-intent.json` and produces an operational summary for the * last 7 days. */ export interface WeeklyDigestEnvelope { /** ISO-8601 start of the 7-day window. */ window_start: string; /** ISO-8601 end of the 7-day window (now). */ window_end: string; /** * Total bytes projected saved over the window (sum of * `projected_savings_pct * total_bytes_proxy`). This is a directional * number derived from the tick projected_savings_pct values — the * ground-truth byte count lives in the verify runner path; for a * quick operational digest the projection is sufficient. * * Null when the history is empty (no ticks ran). */ total_projected_savings_pct: number | null; /** * Number of ticks that ran during the window. */ tick_count: number; /** * Number of ticks that applied a change (status==='applied'). */ applied_count: number; /** * Per-action count from the current `data/action-intent.json`. * Key is the Action string ('drop' | 'compact' | 'sample' | 'offload' | 'tier_down' | 'pass'). */ action_distribution: Record; /** * Ordered tick history (oldest first) for ticks that ran within the window. */ tick_history: DigestTickEntry[]; /** * Patterns that are new this week (present in current intent but absent * from the oldest tick snapshot's implied prior state) or that grew >5x * in their savings contribution week-over-week (anomaly). */ pattern_notes: DigestPatternNote[]; /** Caveats (missing history file, parse errors, empty intent, etc.). */ caveats: string[]; /** Rendered markdown digest. Populated when format=weekly_digest. */ markdown?: string; /** One-paragraph plain-prose summary. */ human_summary: string; } /** * Update a Beta(α,β) prior with weekly fractional evidence and return * p10 / expected / p90 of the posterior as percentages. * * Each weekly delivered_pct/100 is treated as a fractional Bernoulli * outcome: it adds `pct_frac` to α and `(1 - pct_frac)` to β. This is * the conjugate-update form when treating "pct of bytes saved per week" * as a continuous Beta-distributed signal. * * Weak default prior α0=β0=2 (spec §5 Q4 default-chosen). Beta quantile * via Wilson-Hilferty Gamma approximation — accurate to ~1pp absolute * error for the percentile reporting we do here. */ export declare function bayesianForwardConfidence(weekly: Array<{ delivered_pct: number; }>, priorAlpha?: number, priorBeta?: number): { p10: number; expected: number; p90: number; low_data_warning: boolean; }; /** * Per-hash descriptor row joined from TSDB. The bullet renderers in the * weekly digest + CFO markdown use this to swap raw 11-char * `pattern_hash` strings for a user-facing `descriptor + service` label * (same primitive `top_patterns` and `pattern_diff` already rely on). */ export interface PatternHashDescriptor { service: string; symbol_message: string; } /** * Resolve `pattern_hash → (service, symbol_message)` for a batch of * hashes using the live customer metrics backend. * * Issues the same `sum by (hash, service, message_pattern)` PromQL that * `estimate-savings` runs (estimate-savings.ts:807) and picks the * dominant (service, descriptor) per hash by bytes, lexicographic * tie-break on service then descriptor. Returns an empty map on any * query error so the renderers degrade to "(unnamed pattern)" rather * than failing the whole report. */ export declare function fetchHashDescriptors(backend: CustomerMetricsBackend, metricsEnv: string, hashes: string[], range: string): Promise>; export declare function executeCommitmentReport(args: CommitmentReportArgs): Promise; export {};