/** * Config-generation closed loop — verify the running engine is executing the * exact cap policy the MCP wrote, not just that a PR merged or a ConfigMap was * applied. * * The MCP writes `caps.csv` into the cap ConfigMap. This module derives a short * deterministic GENERATION id from that policy and writes it as a sibling * `config-generation.csv` key. * * ENGINE-SIDE CONTRACT. The rate receiver reads that file via its * `rateReceiverConfigGenerationFile` option — set by pointing * `configGeneration.file` (or the `CONFIG_GENERATION_FILE` env) at the file, * which the receiver's config-generation object then loads and stamps on every * event as `tenx_config_version=`, riding the summary metrics as a * Prometheus label. So the running engine advertises * which policy generation it loaded. The option is OPT-IN and decoupled from * caps: a receiver pointed at no file (e.g. GitOps-managed caps without this * MCP) emits no label and never depends on the file existing. See * `pipelines/run/receive/rate/config-generation-object.js` (the loader) and * `modules/.../receive/rate/{module.yaml,settings.yaml}` (the option + the * `tenx_config_version` enrichmentField) in the config repo. * * Verification is STATELESS: the generation is a hash of the policy, so the * verifier recomputes the expected generation from the CURRENT cap ConfigMap and * compares it to the label the engine advertises. Match => the engine is running * the current policy. Mismatch => the write happened but the engine hasn't picked * it up (still polling, not reloaded, or crash-looping) — changes written, not * live. This is the control-plane twin of the offload delivery verifier: it * confirms the OUTCOME (policy is live), not just the intent (policy was written). */ /** * Deterministic short generation id for a cap policy. Hash of the policy text so * it changes iff the policy changes; identical policies map to the same id (a * re-apply of the same caps is genuinely the same generation). */ export declare function computeGeneration(capsCsv: string): string; /** * The `config-generation.csv` body the receiver's config-generation object reads * (`TenXLookup.get("rateReceiverConfigGenerationFile", "generation", "key", "value")`). * Two columns with a header, mirroring caps.csv's shape. */ export declare function renderGenerationCsv(generation: string): string; export type ConfigLiveVerdict = 'live' | 'stale' | 'unverified' | 'not_configured'; export interface ConfigLiveResult { verdict: ConfigLiveVerdict; /** Hash of the current cap policy — what SHOULD be live. */ expected_generation: string | null; /** Generation(s) the running engine(s) advertise on the wire. */ running_generations: string[]; message: string; } export interface ConfigLiveDeps { /** Current caps.csv from the cap ConfigMap (what the MCP last wrote). null if absent. */ readCapsCsv(): Promise; /** * The set of `tenx_config_version` label values active in the recent window. * A set (not one value) so a rollover with old+new pods lingering is handled: * `live` as soon as the expected generation appears, even before old pods cycle. */ readRunningGenerations(): Promise; } export declare function verifyConfigGeneration(deps: ConfigLiveDeps): Promise;