Documentation
¶
Overview ¶
Package eval scores an audit against a dataset whose defects are already known.
A dataset with a manifest is a dataset somebody has already worked out the answers for: every problem planted in it, and every place it is deliberately clean. That turns two questions that were previously matters of opinion into numbers. For the deterministic checks it answers "did this change break anything", which the test suite has always asked. For the agent it answers the question the test suite cannot: a model is nondeterministic, two runs of the same model on the same data find different things, and "it found a defect" is not the same claim as "it found the defects". Scoring a defect set across repeated runs is the only way to tell those apart.
The manifest lives beside the data it describes, as one file, because a second list of the same defects would eventually disagree with the first — and then a passing test would mean nothing.
Index ¶
- Constants
- func CoverageOf(targets []TargetScore) float64
- func MatchesTarget(f finding.Finding, d Defect) bool
- func WriteJSON(w io.Writer, s Score) error
- func WriteText(w io.Writer, s Score) error
- type AgentTarget
- type ChecksScore
- type Claim
- type Clean
- type Context
- type Defect
- type Manifest
- type Noise
- type Options
- type RunScore
- type Score
- func (s Score) Aided() []TargetScore
- func (s Score) Coverage() float64
- func (s Score) MeanRecall() float64
- func (s Score) MeanRecallOf(targets []TargetScore) float64
- func (s Score) Scored() int
- func (s Score) Unaided() []TargetScore
- func (s Score) UnclassifiedClaims() []Claim
- func (s Score) Unscored() int
- type TargetScore
Constants ¶
const FileName = "veritix-manifest.yaml"
FileName is the manifest's conventional name inside a dataset directory.
Variables ¶
This section is empty.
Functions ¶
func CoverageOf ¶
func CoverageOf(targets []TargetScore) float64
CoverageOf is the fraction of a subset of targets found by at least one run.
func MatchesTarget ¶
MatchesTarget reports whether a finding measures a manifest target, regardless of what produced it.
This is the whole of what "found it" means, in one place, because two callers ask the question for opposite reasons: the scorer asks whether to credit a model, and the check suite asks whether a deterministic rule has started covering a defect the model is still being credited for. A target caught by both would quietly inflate every score after it.
func WriteJSON ¶
WriteJSON renders a scorecard for a machine: a run recorded over months is how a model choice gets defended later, and that wants a stable shape rather than a terminal layout somebody has to parse back.
func WriteText ¶
WriteText renders a scorecard for a terminal.
It leads with the two numbers that are easy to conflate. A single figure for "how good is this model at auditing" would be the wrong shape of answer: what an operator is choosing between is a model that reliably finds the same two defects and one that finds two of five at random, and those can share a mean.
Types ¶
type AgentTarget ¶
type AgentTarget struct {
// Count is what a correct measurement of this defect returns.
Count int64 `yaml:"count"`
// Equivalent are other true measurements of the same defect.
//
// Two orphaned rows sharing one bad code are two rows and one value, and a
// model that counts distinct offenders rather than affected rows has found
// the same defect. Where a target admits more than one true figure the
// manifest has to say so: the alternative is a correct model scoring zero,
// which would show up as the model's failure rather than the fixture's.
//
// This is a concession and it should stay a small one. A target whose
// figure genuinely depends on how the question is asked is a target that
// cannot be credited reliably, and the fixture is usually the thing to fix.
Equivalent []int64 `yaml:"equivalent"`
// Query is a SELECT returning Count, so the manifest's own claim can be
// re-run rather than believed.
//
// It is not shown to the model and it is not how a finding is credited —
// the model has to write its own query and the engine has to run that one.
// It is here because a target with a wrong count is a target nothing can
// ever match, and the eval would report a model missing it forever without
// anything saying why. Evidence that re-runs is the rule everywhere else in
// Veritix, and a manifest making claims about a dataset is not the place to
// make it an exception.
Query string `yaml:"query"`
}
AgentTarget describes what a model has to produce to be credited with a defect.
The count is the load-bearing field, and it is deliberately the engine's number rather than anything the model wrote. A model's own rule slug and title are prose: two runs name the same defect two ways, and scoring against prose would measure vocabulary. What the engine measured from the model's own count_query is not prose, so a finding at the right location measuring the right number is the same claim however it was worded.
func (*AgentTarget) Measures ¶
func (a *AgentTarget) Measures(n int64) bool
Measures reports whether a figure is a true measurement of this defect.
type ChecksScore ¶
type ChecksScore struct {
// Found are the ids of defects their nominated rule caught.
Found []string
// Missed are the defects a rule was supposed to catch and did not.
Missed []Defect
// FalsePositives are clean locations where a check fired anyway.
FalsePositives []Clean
// Uncovered are the defects no check proposes, listed rather than
// counted: they are not failures of the deterministic pass, they are the
// reason the agentic tier exists.
Uncovered []Defect
// Converted are agent targets an accepted rule now measures, so the
// deterministic pass finds them and no model is needed.
//
// They are kept apart from Found, which credits a built-in check named in
// the manifest, and apart from the model's recall, which stays
// agent-origin only. Folding either way would lose the fact worth
// reporting: a target here was the agent's to find and is not any more,
// because somebody accepted a rule. That is the whole return on paying a
// model to audit data — once per class of defect rather than once per
// audit — and it is invisible in every other number on the scorecard.
Converted []string
}
ChecksScore is how the deterministic pass did against the manifest.
func ScoreChecks ¶
func ScoreChecks(m *Manifest, findings []finding.Finding) ChecksScore
ScoreChecks measures a finding set against the deterministic half of a manifest.
func (ChecksScore) Complete ¶
func (s ChecksScore) Complete() bool
Complete reports whether the checks did everything the manifest asks of them.
type Claim ¶
type Claim struct {
Rule string
Where string
Count int64
// Covers names the deterministic rule already responsible for this
// location, when one is. A model spending its budget restating what the
// checks found is not wrong, but it is not doing the job the agentic tier
// was added for, and averaging that into "unclassified" would hide it.
Covers string
// Known is a manifest noise entry's reason, when the claim is one somebody
// has already adjudicated. It explains the claim; it does not grade it.
Known string
}
Claim is an agent finding the manifest does not account for.
It is reported, not penalized. Every agent finding has already been measured by the engine and re-verified by finding.Set.Verify, so an unclassified claim is a true statement about the data that nobody thought to plant — which is either a defect the manifest should gain, or the model finding something trivially true and calling it a problem. Only a person can tell those apart, so the scorecard shows them and declines to grade them.
type Clean ¶
type Clean struct {
// Rule is the check that must not fire here.
Rule string `yaml:"rule"`
// Where is the location, in the same form as Defect.Where.
Where string `yaml:"where"`
// Why explains why this location is clean.
Why string `yaml:"why"`
}
Clean is a place the dataset is deliberately correct.
type Context ¶
type Context struct {
// ID names the document, and is what a target's NeedsContext refers to.
ID string `yaml:"id"`
// File is its path relative to the manifest's own directory.
File string `yaml:"file"`
// Why says what this document carries and which targets need it.
Why string `yaml:"why"`
}
Context is one document from a customer's own systems — a data dictionary page, a warehouse catalog, a ticket.
It is not data and it is not ingested: it lives beside the dataset in a form file discovery does not recognize, and it reaches a model only when something fetches it. That is the whole point of listing it here. A defect that is invisible in the export and obvious once the dictionary is read is the case Veritix's deterministic tier cannot reach by construction, and until a fixture contained one there was no way to tell an agent that uses the customer's context from one that ignores it.
A document states the rule and never the violation. One that named the offending row would be handing over the answer, and the fixture would measure whether a model can copy an id out of a paragraph.
type Defect ¶
type Defect struct {
// ID names the defect. It appears in the scorecard and is stable.
ID string `yaml:"id"`
// Where is the location, as "<display>" or "<display>.<column>".
Where string `yaml:"where"`
// Why explains the defect to a person reading a failure.
Why string `yaml:"why"`
// CaughtBy is the deterministic rule that must find it, or "none" when no
// check proposes it.
CaughtBy string `yaml:"caught_by"`
// Agent is set when a model is expected to find this one.
Agent *AgentTarget `yaml:"agent"`
// NeedsContext names the context documents without which this defect is
// invisible. Empty means the export alone is enough to find it.
//
// It is on the defect rather than on the target because it is a claim
// about the dataset, not about the model: these rows are indistinguishable
// from correct ones until something outside the export says what the
// column is for. Splitting the scorecard on it is what turns "context
// helped" from an impression into a number, and — because a fixture also
// carries targets that need nothing — lets the same run show whether
// filling the transcript with documents cost the model the ones it could
// already find.
NeedsContext []string `yaml:"needs_context"`
}
Defect is one problem placed on purpose.
func (Defect) Deterministic ¶
Deterministic reports whether a check is supposed to catch this defect.
type Manifest ¶
type Manifest struct {
// Version is the document format version. Only 1 exists.
Version int `yaml:"version"`
// Dataset names the fixture, for the scorecard's header.
Dataset string `yaml:"dataset"`
// Description says what the dataset is for.
Description string `yaml:"description"`
// Defects are the problems planted in it.
Defects []Defect `yaml:"defects"`
// Clean are places a check must stay quiet. A check that fires on
// everything is useless, and only this half of the manifest catches one.
Clean []Clean `yaml:"clean"`
// Noise are true observations that are not defects, so the scorecard can
// say so instead of asking a reader to adjudicate the same claim again
// every run.
Noise []Noise `yaml:"noise"`
// Context are the customer's own documents, for a fixture whose defects
// are not all in the data.
Context []Context `yaml:"context"`
// Dir is the directory the manifest was loaded from, so a context
// document's relative path can be resolved. It is not part of the
// document.
Dir string `yaml:"-"`
}
Manifest is the ground truth for one dataset.
func Load ¶
Load reads the manifest at a path. If the path is a directory, the conventional file name inside it is used.
func (*Manifest) AgentTargets ¶
AgentTargets is the subset of defects a model is expected to find.
func (*Manifest) ContextPath ¶
ContextPath resolves a context document against the manifest's directory.
func (*Manifest) ReadContext ¶
ReadContext returns one context document's text.
This is what a source of the customer's context reads in a test: the documents are files here because a fixture has to be committed, and whatever serves them in earnest — an MCP server on the customer's own network — hands back the same bytes.
type Noise ¶
type Noise struct {
// Where is the location, in the same form as Defect.Where.
Where string `yaml:"where"`
// Count is the figure the engine returns for it.
Count int64 `yaml:"count"`
// Why explains why this is not a defect.
Why string `yaml:"why"`
}
Noise is something a model can truthfully report that nobody should act on.
Clean entries police the checks, whose rule names Veritix chose. They cannot police an agent claim: the rule slug is model-authored prose, and two runs word the same observation two ways -- gpt-oss-120b called the same thing inconsistent_status_length once and mixed_status_format the next time. So a noise entry is keyed the way a target is keyed, on the engine's number at a location, which is the one part of a claim the model does not write.
It labels, and deliberately does not penalize. Scoring a model down for noticing something true would be grading its judgment through its wording, which is the thing MatchesTarget exists to avoid. A claim that matches a noise entry has already failed to match every target, so this can never absolve a real hit.
type Options ¶
type Options struct {
// Paths are the files and directories to audit as one dataset.
Paths []string
// Manifest is the ground truth to score against.
Manifest *Manifest
// Engine configures the DuckDB instance and its limits.
Engine config.Engine
// Profile controls the depth of profiling.
Profile profile.Options
// Rules are expectations to apply before scoring — in practice, rules
// accepted from an earlier run's proposals.
//
// This is what makes the rule-proposal loop measurable rather than merely
// plausible. A model finds a defect on one run in three; a rule accepted
// from that run finds it on every run, with no model. Scoring an audit
// with those rules loaded is the only way to see the conversion, and the
// scorecard reports it separately from what the model found so that an
// accepted rule can never be mistaken for the model earning its keep.
Rules *rules.File
// Agent is the model under evaluation. Nil scores the deterministic
// auditor alone, which is a useful thing to measure and the only thing CI
// can measure without a model.
Agent *agent.Options
// Context names the MCP servers the agent may read the customer's own
// documents from. It is what the aided half of a scorecard measures: the
// same fixture, the same model, scored with the documents reachable and
// without them.
Context config.Context
// Runs is how many times to audit the dataset. One is enough for the
// deterministic pass and is not enough for a model.
Runs int
}
Options controls an evaluation.
type RunScore ¶
type RunScore struct {
// Detected and Missed are agent-target defect ids.
Detected []string
Missed []string
// Unclassified are agent findings that matched no target.
Unclassified []Claim
// Checks is how the deterministic pass did on the same run.
Checks ChecksScore
// Trace is what the agent run cost, when one ran.
Trace *agent.Trace
// Err is set when the run itself failed.
Err string
}
RunScore is one audit scored against the manifest.
func ScoreRun ¶
ScoreRun credits a run's agent findings against the manifest's targets.
A finding is credited when it sits at the target's location and the engine measured the target's count. Neither half is enough on its own: a location match alone would credit any observation about the column, and a count match alone would credit a coincidence elsewhere in the dataset.
func (RunScore) Scorable ¶
Scorable reports whether this run says anything about the model.
A run that ended because the model stopped, or because a budget did, is evidence: the model had its chance and either used it or spent it badly. A run that ended because the provider stopped answering is not. The first measurement taken with this harness averaged a 30-minute request timeout in as a zero, which measured the machine the model was running on.
Such runs are kept and printed. They are only left out of the averages, and the scorecard says how many.
type Score ¶
type Score struct {
Dataset string
Provider string
Model string
Runs []RunScore
Targets []TargetScore
// Checks is the deterministic result, taken from the first run that
// completed. It is reported once because it is supposed to be the same
// every time.
Checks ChecksScore
// ChecksUnstable is set when it was not. That is a defect in Veritix
// rather than a score for the model, and it invalidates the run it appears
// in, so it is surfaced rather than averaged away.
ChecksUnstable bool
}
Score aggregates repeated runs over one dataset.
Two numbers matter and they are not the same number. MeanRecall is what a single audit can be expected to find. Coverage is what the model finds given enough attempts. When they diverge — half the defects per run, all of them across runs — the model is picking a different one each time rather than finding some and missing others, and no single run is evidence of either.
func Run ¶
Run audits a dataset repeatedly and scores each pass against the manifest.
Repetition is the whole point when a model is configured. One run of an agent is an anecdote: the same model on the same data takes a different path every time, and a defect it found once it may not find again. Repeating the audit is the only instrument that distinguishes a model that finds half the defects from one that finds a different half each time.
A run that fails is recorded and the evaluation continues. Losing four completed runs because the fifth timed out would be the worst possible way to spend an hour of a local model's time.
func (Score) Aided ¶
func (s Score) Aided() []TargetScore
Aided is the targets that need one of the customer's own documents to be visible at all, and Unaided the rest.
The split is the instrument M5b is built against, and it only says anything because both halves are measured on the same runs of the same fixture. Recall over the aided half answers whether fetching the customer's context bought anything. Recall over the unaided half is the control: those targets were findable before any document was loaded, so a run that scores worse on them with the context turned on has found a regression — a transcript full of documents crowding out the work — and without the second number that would show up as the aided half looking good.
func (Score) MeanRecall ¶
MeanRecall is the average fraction of targets found per scorable run.
func (Score) MeanRecallOf ¶
func (s Score) MeanRecallOf(targets []TargetScore) float64
MeanRecallOf averages per-run recall over a subset of targets.
It is computed from the same per-run detections the overall figure is, not from the target hit rates, because the two are not the same number: a rate per target says how often each defect was found, and recall says how much of the set one audit can be expected to find. Averaging rates would report the first and label it the second.
func (Score) Scored ¶
Scored is how many runs the averages are over, and Unscored how many were left out because nothing about them was the model's doing.
func (Score) Unaided ¶
func (s Score) Unaided() []TargetScore
Unaided is the targets the export alone is enough to find.
func (Score) UnclassifiedClaims ¶
UnclassifiedClaims collects every claim across runs, most frequent first.
type TargetScore ¶
type TargetScore struct {
Defect Defect
// Hits is how many runs found it, out of Runs.
Hits int
Runs int
}
TargetScore is how one defect fared across repeated runs.
func (TargetScore) Rate ¶
func (t TargetScore) Rate() float64
Rate is the fraction of runs that found this defect.