/**
 * Trace contracts — finite-trace temporal assertions over span sequences.
 *
 * Five LTLf operators over one ordered span sequence — `always(p)`,
 * `never(p)`, `eventually(p)`, `precedes(a, b)`, `neverUnless(p, prior)` —
 * deterministic and judge-free. No nesting: each rule is one operator over
 * flat `SpanPredicate`s; compose richer checks with multiple rules.
 *
 * A built `TraceContract` is a serializable plain object (RegExp matchers
 * are normalized to `SerializedRegex`), so ONE contract definition is
 * dual-use:
 *
 *   - recorded eval traces — `evaluateTraceContract(contract, await
 *     store.spans({ runId }))`, or via the behavior DSL:
 *     `expectAgent(store, runId).toSatisfyContract(contract)`.
 *   - the production OTLP stream — `ExportableSpan`s flattened by
 *     `trace/otel-bridge` satisfy `ContractSpan` structurally. A production
 *     monitor implements `OtelExporter`, buffers `exportSpan` payloads per
 *     trace, and runs `checkTraceContracts(buffer, contracts)` on flush:
 *
 *       const buffer: ContractSpan[] = []
 *       const monitor: OtelExporter = {
 *         exportSpan: (s) => { buffer.push(s) },
 *         flush: async () => {
 *           const { allValid, verdicts } = checkTraceContracts(buffer, contracts)
 *           if (!allValid) alert(verdicts)
 *         },
 *         shutdown: async () => {},
 *       }
 *       const store = createOtelTracingStore(inner, monitor, runId)
 *
 * `custom` predicate functions are the one non-serializable escape hatch:
 * the builder stamps `requiresCustom: true` (which DOES survive JSON) so a
 * deserialized contract that lost its function fails loud at evaluation
 * instead of silently weakening.
 *
 * Naming: the root barrel exports ci-gate's threshold-contract
 * `evaluateContract`, so the evaluators here are `evaluateTraceContract` /
 * `checkTraceContracts`.
 */
import type { JudgeConfig, Scenario } from './campaign/types';
import type { DefaultVerdict } from './verdict';
/**
 * Minimal structural span the checker reads. Both the eval-side `Span`
 * (trace/schema) and the OTLP-flattened `ExportableSpan` (trace/otel-export)
 * satisfy it; any other producer only needs these fields.
 */
export interface ContractSpan {
    spanId?: string;
    name?: string;
    kind?: string;
    startedAt?: number;
    status?: string;
    error?: string;
    /** Typed field on eval-side ToolSpans; OTLP flattenings drop it (see
     *  `tool` matching order in {@link matchSpan}). */
    toolName?: string;
    attributes?: Record<string, unknown>;
}
/** JSON-safe RegExp form — what the builder normalizes RegExp matchers to. */
export interface SerializedRegex {
    $regex: string;
    flags: string;
}
export type TextMatcher = string | RegExp | SerializedRegex;
/**
 * Proposition over one span. All specified fields must match (AND).
 * At least one field is required — an empty predicate would match every
 * span and is rejected.
 *
 * `tool` resolution order covers both span shapes: `span.toolName` (typed
 * ToolSpan) → `attributes['tool.name']` / `attributes['toolName']`
 * (OTLP-flat attribute conventions) → `span.name` when `kind === 'tool'`
 * (otel-bridge's `ExportableSpan`, which drops `toolName`).
 *
 * `attr` values match by strict equality, or regex-test when the value is a
 * RegExp/SerializedRegex and the attribute is a string. Structured attribute
 * values need `custom`.
 */
export interface SpanPredicate {
    name?: TextMatcher;
    tool?: TextMatcher;
    attr?: Record<string, unknown>;
    custom?: (span: ContractSpan) => boolean;
    /** Stamped by the builder when `custom` is present. Survives JSON while
     *  the function does not, so evaluation of a deserialized contract throws
     *  instead of silently dropping the check. */
    requiresCustom?: true;
}
export type ContractRuleKind = 'always' | 'never' | 'eventually' | 'precedes' | 'neverUnless';
export interface ContractRule {
    kind: ContractRuleKind;
    /** Unique within the contract — keys the per-rule score. */
    label: string;
    /** Subject predicate for always / never / eventually / neverUnless. */
    p?: SpanPredicate;
    /** precedes: the required precondition. */
    a?: SpanPredicate;
    /** precedes: the guarded match — every b-match needs an earlier a-match. */
    b?: SpanPredicate;
    /** neverUnless: the authorizing earlier match. */
    prior?: SpanPredicate;
}
/** Serializable plain object — `traceContract(name)....build()` output. */
export interface TraceContract {
    name: string;
    rules: ContractRule[];
}
export interface ContractViolation {
    rule: string;
    spanId?: string;
    detail: string;
}
export interface ContractVerdict extends DefaultVerdict {
    /** Contract name — keys this verdict in multi-contract reports. */
    contract: string;
    valid: boolean;
    /** Fraction of rules passing, in [0, 1]. */
    score: number;
    /** Per-rule 0|1 keyed by rule label. */
    scores: Record<string, number>;
    violations: ContractViolation[];
}
export interface ContractCheckResult {
    verdicts: ContractVerdict[];
    allValid: boolean;
}
/** Test one span against one predicate. All specified fields must match. */
export declare function matchSpan(span: ContractSpan, predicate: SpanPredicate): boolean;
export declare class TraceContractBuilder {
    private readonly name;
    private readonly rules;
    constructor(name: string);
    /** Every span in the trace must satisfy `p`. */
    always(p: SpanPredicate, label?: string): this;
    /** No span in the trace may satisfy `p`. */
    never(p: SpanPredicate, label?: string): this;
    /** At least one span in the trace must satisfy `p`. */
    eventually(p: SpanPredicate, label?: string): this;
    /** Every `b`-match must have a strictly earlier `a`-match. */
    precedes(a: SpanPredicate, b: SpanPredicate, label?: string): this;
    /** Every `p`-match is a violation unless a strictly earlier `prior`-match exists. */
    neverUnless(p: SpanPredicate, prior: SpanPredicate, label?: string): this;
    build(): TraceContract;
    private add;
}
export declare function traceContract(name: string): TraceContractBuilder;
/**
 * Evaluate one contract over a span sequence. Pure and synchronous — works
 * on `Span[]` from a TraceStore, `ExportableSpan[]` from the otel-bridge
 * flattening, or any array satisfying `ContractSpan`.
 */
export declare function evaluateTraceContract(contract: TraceContract, spans: readonly ContractSpan[]): ContractVerdict;
/**
 * Evaluate many contracts over one span sequence. Throws on an empty
 * contract list — `allValid: true` over zero contracts is a silent pass.
 */
export declare function checkTraceContracts(spans: readonly ContractSpan[], contracts: readonly TraceContract[]): ContractCheckResult;
export interface ContractJudgeOptions<TArtifact, TScenario extends Scenario = Scenario> {
    /**
     * Project the span sequence out of a cell's artifact. `JudgeConfig.score`
     * receives only `{ artifact, scenario, signal }` (src/campaign/types.ts) —
     * spans are NOT reachable generically — so the consumer supplies this
     * explicit extraction (e.g. dispatch writes spans into the artifact, or
     * closes over a per-cell TraceStore read).
     */
    spans: (input: {
        artifact: TArtifact;
        scenario: TScenario;
    }) => readonly ContractSpan[];
    /** Judge name in campaign reports. Default 'trace-contracts'. */
    name?: string;
}
/**
 * Adapt trace contracts to a campaign `JudgeConfig`. One judge dimension per
 * contract (key = contract name, value = its rule-pass fraction); composite
 * is the mean across contracts. Deterministic — no LLM call.
 */
export declare function contractJudge<TArtifact, TScenario extends Scenario = Scenario>(contracts: readonly TraceContract[], opts: ContractJudgeOptions<TArtifact, TScenario>): JudgeConfig<TArtifact, TScenario>;
//# sourceMappingURL=trace-contracts.d.ts.map