import type { CortexStore, TableCounts } from '../db/store.js'; import { type EvaluationScenario } from './seed.js'; export interface TextMetric { chars: number; est_tokens: number; preview: string; } export interface TopicEvaluation { topic: string; output: TextMetric; } export interface QualityFixture { topic: string; expected_top: string; allowed?: string[]; forbidden?: string[]; max_output_tokens?: number; fresh_after?: string; /** Substrings that must appear in the rendered recall output (e.g. staleness labels). */ expect_output_contains?: string[]; /** Substrings that must not appear in the rendered recall output. */ expect_output_excludes?: string[]; } /** * An assertion on a whole rendered **surface**, rather than on a recall query. * * The gate compared `top1_hit`, `recall_at_3` and `output_tokens` from * topic-driven fixtures only. `header` and `full_state` were computed on every * run and **nothing ever looked at them**, and the SessionStart brief was not * computed at all — so a change to any of those three could not turn the gate * red. Story 3.4 edits the brief, which made shipping without this the same as * shipping untested. * * Deliberately not a retrieval metric: these are `contains`/`excludes`/token * assertions on rendered text, so they can fail a suite without touching the * three gated numbers, and every existing baseline stays byte-identical. */ export type EvaluatedSurface = 'brief' | 'header' | 'full_state'; export interface SurfaceFixture { surface: EvaluatedSurface; expect_contains?: string[]; expect_excludes?: string[]; max_tokens?: number; /** * Render the brief at this budget instead of its 150 default. * * Without it the token budget cannot be gated at all: a seeded brief is 36–77 * tokens against `max_tokens: 150`, so the assertion has ~100 tokens of * headroom and can never bind — and deleting the enforcement loop outright * left the whole gate green. A fixture that renders at a budget the content * genuinely exceeds is the only way this surface's budget code is exercised. * Ignored for `header` and `full_state`, which take no budget here. */ budget?: number; } export interface SurfaceEvaluation { surface: EvaluatedSurface; output: TextMetric; contains_missed: string[]; excludes_violated: string[]; token_budget: { max_tokens: number | null; actual_tokens: number; passed: boolean; }; passed: boolean; } export interface QualityScoreBreakdown { id: string; subject: string | null; text_preview: string; scores: { retrieval_score: number; lexical_score: number; temporal_bonus: number; scope_bonus: number; kind_bonus: number; recency_bonus: number; hotness_bonus: number; access_bonus: number; token_hits: number; exact_phrase: boolean; fts_rank: number | null; }; } export interface QualityFixtureEvaluation { topic: string; expected_top: string; top_result_id: string | null; top1_hit: boolean; recall_at_3: number; noise_count: number; stale_count: number; output: TextMetric; token_budget: { max_tokens: number | null; actual_tokens: number; passed: boolean; }; output_assertions: { contains_missed: string[]; excludes_violated: string[]; passed: boolean; }; score_breakdown: QualityScoreBreakdown[]; passed: boolean; } export interface QualityEvaluation { fixtures: QualityFixtureEvaluation[]; top1_hit: number; recall_at_3: number; noise_count: number; stale_count: number; output_tokens: number; } export interface QualityComparison { top1_hit_delta: number; recall_at_3_delta: number; noise_count_delta: number; stale_count_delta: number; output_tokens_delta: number; } export interface EvaluationOptions { fixtures?: QualityFixture[]; surfaces?: SurfaceFixture[]; compareTo?: EvaluationResult; /** Hermetic seed: evaluate against an in-memory store built from this scenario. */ scenario?: EvaluationScenario; } export interface EvaluationResult { db_path?: string; generated_at: string; schema_version: number; tables: TableCounts; header: TextMetric; full_state: TextMetric; /** * The SessionStart brief. Recorded even when no suite asserts on it, because * a surface the harness does not compute is a surface no future fixture can * reach — the exact reason this one went unmeasured until Story 3.4. */ session_brief: TextMetric; topics: TopicEvaluation[]; quality?: QualityEvaluation; quality_comparison?: QualityComparison; surfaces?: SurfaceEvaluation[]; } export declare function estimateTokens(text: string): number; export declare function buildTextMetric(text: string, previewLength?: number): TextMetric; export declare function evaluateStore(store: CortexStore, requestedTopics: string[], dbPath?: string, options?: EvaluationOptions): EvaluationResult; export declare function evaluateDatabase(dbPath: string, rootPath: string, requestedTopics: string[], options?: EvaluationOptions): EvaluationResult; //# sourceMappingURL=harness.d.ts.map