/**
 * column-types — the tool declared what its rows contain, and the rows say
 * otherwise. Filed at the WRITE seam, at the moment the tool answers.
 *
 * Pattern: pure function over (one declaration, one finished rowset); the
 *          SHAPE check, one storey below `empty-lookup` — that one asks
 *          whether the answer had any rows, this one asks what is in them.
 * Role:    the decidable fragment of "is this rowset the rowset it claims to
 *          be?".
 *
 * ── THE MEASURED FAILURES. Three, and they are one shape ────────────────
 *
 *  1. A mapping report wrote `str(m.get("logical_unit_number") or "")`. LUN 0
 *     is falsy, so on 2,094 mappings LUN 0 was stored as an EMPTY STRING —
 *     and a host group missing the LUN an initiator probes first became
 *     indistinguishable from one that had it. The column was numeric; the
 *     value was `''`; nothing anywhere disagreed.
 *  2. A capacity view rendered `round(mib / 1024, 1)`, so an 8 MiB disk came
 *     out as `0.0 GB` — which reads as NO DISK, i.e. a provisioning failure,
 *     during a live desktop-fleet incident.
 *  3. Earlier in the same application, a whole family of tools returned their
 *     numbers as quoted strings (`"1240"`). Every chart silently went blank,
 *     because nothing downstream could tell a measure from a label.
 *
 * All three are "a number became something else, and nothing noticed at the
 * seam". The library already lets a tool declare what its result IS
 * (`resultKind`, 9.70.0). It did not let a tool declare what its result
 * CONTAINS, so there was nothing for a rowset to be wrong against.
 *
 * ── THE CEILING, and case 2 is the whole argument for stating it ────────
 * This judges TYPE, never MEANING. It can see that a column declared
 * `number` holds a string. It can NEVER see that the string should have been
 * `0`, or that `0.0` should have been `0.0078`. Case 2 above passes this
 * check cleanly — `0.0` is a perfectly good number — and the check says so
 * out loud rather than letting a green row imply otherwise. The ceiling ships
 * as {@link COLUMN_TYPE_CEILING} and is quoted verbatim into every finding,
 * the `EMPTY_LOOKUP_CEILING` law: one owner for the bound, so it cannot drift
 * out of a message and leave a reader believing the library knows more than
 * it does.
 *
 * ── TWO FINDINGS, because the field bug turned on the difference ────────
 *   • `column-type-mismatch` — the column is THERE and holds the wrong thing.
 *     Cases 1 and 3.
 *   • `missing-column` — the column the author declared is in NO row of the
 *     result. Nothing to type-check; the promise was broken one level up.
 * Collapsing them would recreate the exact ambiguity the LUN report died of:
 * "the value is not what it should be" and "the value is not there" send a
 * person to two different files, and a checker that says only "something is
 * off with logical_unit_number" has helped with neither.
 *
 * ── DECLARED, NEVER INFERRED ────────────────────────────────────────────
 * A tool is this check's subject only because its author wrote
 * `resultColumns`. Nothing here sniffs a type off the data — sniffing is
 * precisely what the consumers do today, and precisely what produced the
 * failures above: one stray `''` demotes a numeric column to text, and the
 * demotion is silent.
 *
 * ── WHAT IS NOT JUDGED ──────────────────────────────────────────────────
 * See {@link readRowset}. A result is read only when it is an array of plain
 * objects with at least one row. Everything else — prose, a `null`, a bespoke
 * `{ rows: [...] }` wrapper, a claim ticket, AND the zero-row result — is
 * `not-applicable`, filed as a ROW. The zero-row case belongs to the
 * neighbour (`empty-lookup`) and is deliberately not stolen: an empty result
 * has no columns to be wrong about, and filing `missing-column` for every
 * declared column of an empty answer would turn one honest emptiness into a
 * pile of false accusations.
 */
import type { Disposition } from '../disposition/types.js';
import type { ContextError } from '../finding/types.js';
import { type ColumnType, type ToolResultColumns } from './types.js';
/**
 * THE CEILING, as one string with one owner.
 *
 * Quoted verbatim into every finding's message, into this folder's README and
 * into the docs page, so the bound cannot drift out of one of them.
 */
export declare const COLUMN_TYPE_CEILING: string;
/** How the boundary was told to act on what this finds. */
export type ColumnCheckMode = 'warn' | 'enforce';
/**
 * What the library could read about a finished result: the rows, or nothing.
 *
 * Deliberately thin. The check needs the rows and nothing else, and a reading
 * that carried a verdict would be this file judging in two places.
 */
export interface RowsetReading {
    readonly rows: readonly Readonly<Record<string, unknown>>[];
}
/**
 * READ a finished result as a rowset, or decline to.
 *
 * The one readable shape, and why only this one: an ARRAY OF PLAIN OBJECTS
 * with at least one row. That is what a rowset is on this wire, it is what
 * every consumer named in the docs page already expects, and it is the same
 * `Array.isArray` law the neighbouring check reads by — the two must never
 * disagree about what a rowset is.
 *
 * `undefined` (⇒ `not-applicable`, a ROW) for everything else:
 *   • a non-array — prose, a `null`, a `{ rows: [...] }` wrapper, a ticket;
 *   • an array holding anything that is not a plain object — a list of
 *     strings has no columns, and inventing some is how a checker starts
 *     lying;
 *   • an array of ZERO rows — an empty answer has no columns to be wrong
 *     about, and it is the neighbour's subject, not this one's.
 */
export declare function readRowset(value: unknown): RowsetReading | undefined;
/** One finished call to a tool whose author declared its result's columns. */
export interface ColumnTypesCall {
    readonly toolName: string;
    /** The provider's id for this call — what the witness points a reader at. */
    readonly toolCallId: string;
    /** `Tool.resultColumns`, exactly as the author wrote it. */
    readonly columns: ToolResultColumns;
    /** What the library could read of the result — `undefined` = not a rowset. */
    readonly reading: RowsetReading | undefined;
    /** What the boundary will do with a finding — carried so the message can
     *  say what actually happened rather than guess. */
    readonly mode: ColumnCheckMode;
}
/** One declared column the rows disagreed with. */
export interface ColumnViolation {
    readonly column: string;
    readonly declared: ColumnType;
    /** How many rows hold something that is not the declared type. */
    readonly rows: number;
    /** Total rows read, so a reader can see 3-of-4 rather than a bare 3. */
    readonly ofRows: number;
    /** The first offending value, rendered and clipped — what a person recognizes. */
    readonly sample: string;
    /** What that first offending value actually is (`string`, `null`, `missing`, …). */
    readonly got: string;
}
/** One encounter's outcome: the findings, the ledger row, and — under
 *  `enforce` — the sentence that replaces the payload. */
export interface ColumnTypesEncounter {
    readonly findings: readonly ContextError[];
    /** Computed HERE, beside the rules that decide it, so "the library refused
     *  to judge this shape" is provable without a live agent. */
    readonly disposition: Disposition;
    /**
     * The teaching refusal, present ONLY under `mode: 'enforce'` with at least
     * one finding. The `applyResultCeiling` idiom, for the same reason: the
     * model reads a sentence that says what was wrong and how to fix it, never
     * a stack trace and never a truncated result that reads as complete.
     */
    readonly refusal?: string;
}
/**
 * Judge one finished call to a tool that declared its result's columns.
 *
 * @param call the call, the declaration, and what the library could read of
 *   the result. The caller has already established the tool declared
 *   `resultColumns` and that the dial is on; nothing here re-decides arming.
 * @param epoch the run iteration, stamped on every witness.
 */
export declare function columnTypesOf(call: ColumnTypesCall, epoch: number): ColumnTypesEncounter;
//# sourceMappingURL=check.d.ts.map