/**
 * The COLUMN-TYPE CONTRACT's vocabulary — what a tool may say about the
 * columns of the rowset it returns, and the one rule that judges the saying.
 *
 * Pattern: closed vocabulary + definition-time assert (the `resultCeiling` /
 *          `resultClass` law: a declaration this library cannot honour fails
 *          at `defineTool`, never at the first row of the first run).
 * Role:    leaf. `src/integrity/` imports nothing outside itself, so the
 *          vocabulary lives HERE and `tools/tools.ts` reaches in for it —
 *          never the other way round.
 *
 * ── Why the words are these words ───────────────────────────────────────
 * They are NOT invented. `number | string | boolean | date` is the vocabulary
 * this ecosystem's rowset consumers already speak — a column-type union with
 * exactly these members is what the visualization layer sniffs its way to
 * today. Declaring in the consumer's own words is the `resultKind` law
 * (9.70.0) restated one level down: the tool's result in the CONSUMER's
 * vocabulary, not the framework's.
 *
 * The one member deliberately NOT carried over is `unknown`. A sniffer needs
 * that word — it is what "I looked at the values and could not tell" sounds
 * like. A DECLARATION has no use for it: an author who does not know what a
 * column holds should not name the column, and `columns: { x: 'unknown' }`
 * would be a promise about nothing that the checker would then have to
 * pretend to verify.
 *
 * ── Two spellings, one meaning ──────────────────────────────────────────
 * A column maps to a bare type (`lun: 'number'`) or to an object form
 * (`note: { type: 'string', nullable: true }`). That is this library's house
 * pattern for exactly this shape — `CostBudget` takes a bare number or
 * `{ usd, onExceed }`, `artifacts` takes a bare store or `{ store, placement }`
 * — and it is normalized ONCE, here, so every reader downstream sees one
 * shape and no downstream file learns that there were two.
 */
/**
 * What a declared column holds.
 *
 * | word | what a value must be |
 * | --- | --- |
 * | `number` | a JavaScript number that is FINITE — `NaN` and the infinities are a number that means "no number", and a chart handed one draws nothing |
 * | `string` | a JavaScript string, including the empty one (emptiness is meaning, and meaning is above this check's ceiling) |
 * | `boolean` | `true` or `false` — never `'true'`, never `0`, never `1` |
 * | `date` | a valid `Date` instance, or a string `Date.parse` accepts (an epoch NUMBER is a `number`; say so and the axis picker stops guessing) |
 */
export type ColumnType = 'number' | 'string' | 'boolean' | 'date';
/** The closed set, in one place, for the assert and for its own error message. */
export declare const COLUMN_TYPES: readonly ColumnType[];
/** The object spelling of one column's declaration. */
export interface ColumnDeclaration {
    /** What the column holds. */
    readonly type: ColumnType;
    /**
     * `true` — a row of this column may legitimately carry NO VALUE (`null`,
     * `undefined`, or the key simply not set on that row), and such a row is
     * never a type violation.
     *
     * Default `false`, and the default is the strict one ON PURPOSE. The field
     * failure this check is built from was a value that went missing and left
     * an empty string behind; had the same code left a `null` behind, the
     * defect would have been identical and a lenient default would have waved
     * it through. One word turns it off, and every finding names that word — so
     * a legitimate null column costs a one-word edit, while a silent default
     * would cost the bug.
     *
     * `nullable` is a promise about VALUES. It is NOT a promise about the
     * column's existence: a declared column that appears in no row at all is a
     * `missing-column` finding whether or not it is nullable, because the
     * declaration named a column and the result has no such column. The valve
     * for "this column may or may not be there" is to not declare it —
     * unlisted columns are allowed and unjudged.
     */
    readonly nullable?: boolean;
}
/**
 * What a tool declares about the columns of its rowset — column name to type.
 *
 * OPEN, NEVER CLOSED. A declaration is a promise about what it NAMES, not a
 * schema of everything the result may contain: a column nobody listed is
 * allowed and is never judged. Two reasons, and both are the same reason.
 *
 *   • A closed schema punishes the wrong party. The day the backend adds a
 *     column, every one of these tools starts filing findings about a change
 *     that broke nothing — and a check that cries about correct behaviour is
 *     a check people switch off, which is how the failure it exists to catch
 *     gets back in.
 *   • It is the rule the neighbouring boundary already keeps.
 *     `toolArgsValidation` is permissive on keywords it does not know and
 *     enforces `additionalProperties: false` only when an author explicitly
 *     asks for it. Two validators at one seam disagreeing about whether
 *     silence means "allowed" would be a worse defect than either could
 *     catch.
 */
export type ToolResultColumns = Readonly<Record<string, ColumnType | ColumnDeclaration>>;
/** One column's declaration after normalization — the ONLY shape any reader
 *  downstream of `normalizeColumns` ever sees. */
export interface NormalizedColumn {
    readonly name: string;
    readonly type: ColumnType;
    readonly nullable: boolean;
}
/**
 * Both spellings into one list, in declaration order.
 *
 * Order is kept because it is the author's order, and a finding that names
 * columns in the order the author wrote them is a finding they can scan
 * against their own source.
 */
export declare function normalizeColumns(columns: ToolResultColumns): readonly NormalizedColumn[];
/**
 * Refuse a `resultColumns` this library cannot honour, at definition time —
 * naming the tool, the column and the fix.
 *
 * Exported beside {@link ColumnType} and called from `defineTool`, so a
 * misspelled type fails on the line that wrote it rather than at the first
 * rowset of the first armed run. Also called by the MCP ingest
 * (`readToolExtras`) on a bag from a server this process does not control —
 * which is why every read below goes through a fallback: a `null`, a number
 * or an array must reach the teaching refusal, never blow up on the way to
 * it.
 */
export declare function assertResultColumns(toolName: string, columns: unknown): void;
