/** * The crew-side eval RUN history — the durable store behind the studio's Evals section. * * # Why this store exists * * `POST /testing/evals/run` computes a report and answers it, but the daemon kept NOTHING: the * engine's eval path is read-only over the steering store (it writes no claim — `conform` is the * live gate's recorder, not the eval's), and studio held the last report only in a session-local * store that a reload lost. So the Evals section could show ONE ephemeral report, never a history. * This store closes that gap: every run is recorded here, so "are the gaps shrinking?" becomes a * question the section can actually answer. * * # Why crew-side, and why not the QE ledger * * Eval runs judge a STORE, not a workspace — they take no repo/project (the recon route is the * only testing route that is workspace-scoped). So they are DAEMON-scoped state, and they live * where crew's other daemon-scoped durable state lives: under the resolved state home * (`state-home.ts`, the seam project graphs and settings already resolve through), so a `--db` * isolated daemon keeps its eval history isolated too. They are deliberately NOT in the wicked- * ledger `DomainStore` (`.wicked-qe/`): that store is repo-scoped QE ACCEPTANCE evidence with a * fixed table set (`runs`/`verdicts` carry a `scenario_id` FK and an acceptance verdict enum) — * reusing it for governance-eval runs would mean the wrong semantics, not reuse. * * # Shape: a rollup INDEX plus per-run DETAIL files * * /evals/runs.jsonl — one {@link EvalRunSummary} per line, append order (the LIST). * /evals/results/.json — one run's full per-sample results (the DRILLDOWN). * * Listing parses one file (the rollups) — cheap no matter how large a custom corpus's results * are; a drilldown loads exactly one detail file. The detail file is written FIRST, then the index * line is appended, so every listed row has a readable detail (a crash between the two leaves an * orphan detail file, which is harmless and self-garbage-collecting, never a dangling row). * * Posture matches the daemon's other sidecars: LOUD-NON-FATAL. Persisting a run must never fail * the run itself (the report is the contract; the history is additive), and a torn or missing file * degrades one row, never the store — reads skip what does not parse, exactly like the audit trail. */ import type { EvalRunDetail, EvalRunSummary, GovernanceEvalResult } from '../core/types.js'; /** * Where the store's root lives. Precedence mirrors the other crew stores exactly: the explicit * `WICKED_CREW_EVAL_STORE` env override (the more specific instruction the tests pin), then the * daemon's state home — the `--db` parent when the bootstrap configured one, the historical * `~/.wicked-crew` default otherwise (byte-identical without `--db`). */ export declare function defaultEvalStoreRoot(env?: NodeJS.ProcessEnv): string; /** The input a run persists: its rollup provenance plus the results the detail file will hold. */ export interface RecordEvalRunInput extends Omit { results: GovernanceEvalResult[]; } export declare class EvalRunStore { private readonly warn; private readonly now; private readonly mintId; private readonly indexPath; private readonly resultsDir; /** Serializes writes so two concurrent `record()`s never interleave a partial index line or * reorder detail-then-index — the same promise-chain discipline the audit JSONL writer uses. */ private writeTail; constructor(root?: string, warn?: (msg: string) => void, now?: () => number, mintId?: () => string); /** * Record one eval run: write the detail file, then append the rollup index line. Returns the * recorded {@link EvalRunSummary} (with its minted id + timestamp). ASYNC (fs/promises) so the * write never blocks the daemon's event loop on a large results file, and SERIALIZED behind any * in-flight write (the audit-trail pattern) so concurrent eval runs never interleave a partial * line (Copilot #467) — the caller records best-effort and never fails the run on a miss. */ record(input: RecordEvalRunInput): Promise; /** The actual write, in the one order that keeps a listed row always resolvable: detail file * (atomic tmp+rename) FIRST, then the rollup index append (an orphan detail after a crash before * the append is harmless; a dangling index row would not be). */ private writeRun; /** * The run history, newest first. Optional equality filters (`type_filter`, `corpus`) narrow it. * A line that does not parse is skipped (a torn final line after a crash is expected once) — the * rest of the history still lists. */ list(filter?: { type_filter?: string | null; corpus?: string | null; }): Promise; /** One run WITH its full results, or null when the id is unknown (or its detail file is gone). */ get(id: string): Promise; private detailPath; } //# sourceMappingURL=eval-store.d.ts.map