Repository navigation
Let an evaluation declare its own result sinks - #27
Merged
Merged
Conversation
@mic.eval gains sinks=, so an eval file can declare where its results go and `mic run` uses those sinks with no extra flags. Declared sinks merge after the recorder and explicit sinks, open once per instance, and receive only the evaluations that declared them: RunInfo, trial events, events from their sources, and a scoped finish outcome. `mic list` shows declared sink names, preflight validates them without opening, and `mic run --no-declared-sinks` ignores them. Refs TEN-1188 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Executive summary
@mic.evalnow acceptssinks=, so an eval file can declare where its results go andmic runuses those sinks with no extra flags. Declared sinks are scoped: each one receives only the evaluations that declared it.mic run --no-sinksturns them off for a run,mic listshows their names, andmic preflightvalidates them without opening them.Why
Linear: TEN-1188. The microevals results pipeline needs every run, local or CI, to reach one shared sink. Today a custom sink can only be passed to
mic.run(..., sinks=[...])from Python;mic runbuilds only a Braintrust sink. Without this change every eval would need a Python__main__bypassing the CLI.The issue's open questions were decided by the maintainer:
mic rungets an opt-out flag (--no-sinks).Design and implementation
Evaluation.sinks: tuple[ResultSink, ...]is a trailing field that defaults to(). It usescompare=Falsebecause sinks are caller-owned and need not be hashable. All threeevaloverloads acceptsinks: Sequence[ResultSink] = (). No new sink type is introduced.resolve_optionschecks that each declared sink has a non-emptynameand a callableopen. It rejects different instances that share a name and allows the same instance to repeat. Preflight goes throughresolve_options, so it validates declared sinks without opening them.engine.arun).delivery.Scope). Each declared sink carries the set of evaluations that declared it and the source ids those evaluations read.opengets aRunInfofiltered to those evaluations.TrialFinishedis filtered bytask.CaseAccepted,RecordRejected,SourceFinished) are filtered bysource_id. With a shared dataset, the declaring eval's sink still receives the shared cases, because they are its inputs.finishgets an outcome with a scoped summary, scopedsources, and only thetasks[...]requirements for those evaluations. The summary comes from an extraAggregatorper distinct scope, fed the same observations.status,exit_codeandfailuresstay run-wide, because they describe the run.--no-sinksappliesdataclasses.replace(spec, sinks=())to the selection, sorun()/arun()keep their signatures.listaddssinks=a,bto text output and asinksarray to eval rows in--json. It reads onlysink.name.resolve_evaluationsnow returnslist[Evaluation[Any, ...]](it is internal) so the CLI canreplacethe selection under strict typing.RunInfo.tasksconfiguration, becauserun-v4usesadditionalProperties: false. Sink receipts already record which sinks ran.Verification and confidence
Risks: wrong delivery scope (leaking another eval's data, or dropping the declaring eval's shared-source events), duplicate opens in suites, name collisions slipping past the pre-effect check, ordering, and changed failure behaviour.
tests/runtime/test_declared_sinks.py(13 tests):RunInfo, trial events, source events, the finish summary, sources and requirements contain only the declaring eval, while explicit sinks still get everything;writeandfinishfailures: disabled sink, admission stopped, healthy sink finalized, no secret text;listtext and JSON, andrunwith and without--no-sinks.return Truemakes the scoping tests fail.sinks=that keeps the fullEvaluationtype, and a negative fixture rejecting a non-sink.uv run python scripts/verify.py: tests, coverage json/report/gates, strict typing, authoring typing, lint and format all PASS.Compatibility and risks
sinks=keyword, a new trailingEvaluationfield with a default, a new CLI flag, and a newsinkskey on eval rows inmic list --json.run()/arun()are unchanged.mic-run-v4ormic-event-v1.docs/reporting.mdanddocs/cli.md, and the mic skill now tells agents to checkmic listand use--no-sinkswhen publishing is not authorized.mic list. The existing passive-construction rule still applies, and the docs restate it.