feat(generate): eval harness + benchmark corpus (design-only path)#2674
Merged
Conversation
Ships the v0.19 Workstream 1 deliverable for `conduit generate` per docs/design-documents/20260722-conduit-generate.md's Status: the command implementation is deferred to v0.20 (build slot went to the Python SDK), but the acceptance bar it must clear ships now as a committed, reviewable artifact instead of being invented mid-implementation later. - cmd/conduit/internal/generate/testdata/eval_requests.yaml: 28 canonical NL pipeline requests, each grounded in a real built-in connector (file, generator, kafka, log, postgres, s3) with its expected semantic intent (source/destination category, required processor capabilities). docs/generate-benchmark.md renders the same corpus for human review. - A scoring harness (ScoreRun/ScoreMedian) that reports two independent numbers, never collapsed into one: validate-pass rate (against the exact cmd/conduit/internal/validate engine `pipelines validate` uses, via a temp-file shim since validate.Run has no in-memory seam yet) and semantic-intent-match rate (does the candidate's actual connectors/ direction/capabilities match the request, even when it validates cleanly). A missing candidate fails both metrics rather than shrinking the denominator; repeated runs are reduced by median, never best-of. - Fixture tests (score_test.go) prove the scorer correct without a live LLM: known-good candidates score true on both axes; known-bad ones (bad schema, wrong connector, dropped filter, swapped direction, an unknown/ fabricated plugin, malformed YAML, ambiguous multi-source) score false on exactly the axis they're wrong on. No CLI command, MCP tool, or LLM provider client ships here — no new external dependency, per the design doc's deferral. Roadmap: v0.19 Workstream 1 (Agent-native, `conduit generate`). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GQFzakPShAYj8CcwajYDD
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.
Summary
Ships v0.19 Workstream 1's committed deliverable for
conduit generate— perdocs/design-documents/20260722-conduit-generate.md's Status section, the commandimplementation itself is deferred to v0.20 (the v0.19 build slot went to the Python
connector SDK). No
generatecommand, MCP tool, or LLM provider client ships inthis PR. What ships is the frozen acceptance bar the future command must clear:
cmd/conduit/internal/generate/testdata/eval_requests.yaml,rendered for humans in
docs/generate-benchmark.md): 28 canonical natural-languagepipeline requests (≥ 25 required), each grounded in a real built-in connector (file,
generator, kafka, log, postgres, s3 — respecting that
generatoris source-only andlogis destination-only) with its expected semantic intent (source/destinationcategory, required processor capabilities).
ScoreRun/ScoreMedianinscore.go) reporting twoindependent numbers, never collapsed into one:
cmd/conduit/internal/validateengineconduit pipelines validateuses. Sincevalidate.Runhas no in-memory(
RunBytes/RunReader) seam yet (confirmed absent from the tree), this uses thedesign doc's documented fallback: a private (0600) temp file, validated, then
removed (
validate_score.go) — the one place in the package that touches disk.validateuses and checks the actual source/destination connector category andprocessor set against the request's
Expect, even when the candidate validatescleanly (
semantic_score.go). A missing candidate fails both metrics ratherthan shrinking the denominator; repeated runs reduce by median, never
best-of (CLAUDE.md's benchi discipline, applied to model-output scoring).
score_test.go, 19tests): known-good candidates score true on both axes; known-bad ones — bad schema,
wrong connector (still validates), dropped filter, swapped direction, an unknown/
fabricated plugin, malformed YAML, ambiguous multi-source — each score false on
exactly the axis they're wrong on.
Adversarial self-review
Re-read the diff hunting specifically for the classes CLAUDE.md flags:
validateCandidate's temp file is removed viadeferonevery return path, including the write/close error paths (verified each early
return happens after
os.CreateTempand before returning, so the defer is alwaysregistered first).
level down): initially,
connectorCategoriessilently kept the first connectorfound in a role and ignored a second one in the same role — i.e. it would have
guessed instead of flagging ambiguity. Caught in self-review and fixed: a second
same-role connector now makes that role's category permanently ambiguous (
""),which always mismatches a non-empty
Expect. AddedTestScoreSemantic_MultipleSourceConnectors_NeverGuessesACategoryto lock this in.documented "return whatever parsed successfully" contract) is judged on what did
parse, with the error surfaced as an additional issue rather than discarded or
treated as an unconditional hard failure.
os.CreateTemp's per-call random suffixmeans concurrent scoring (not currently done, but not precluded) wouldn't collide.
pipeline, ack/position/checkpoint logic, or any serialized format; it only reads
YAML text it's given and writes/removes its own private temp file.
Gates run (this PR only — see Process maturity table for what's not live)
go build ./...— clean.golangci-lint run ./cmd/conduit/internal/generate/...— 0 issues.go test -race ./cmd/conduit/internal/generate/...— 19/19 pass.make generate && git diff --exit-code— clean (no generated-file drift; this PRadds no
//go:generatedirective).npx markdownlint-cli2 docs/generate-benchmark.md— 0 errors.--jsonCLI surface added (no CLI command ships) — Family-A/golden-fixture gateN/A.
allcodes.goregistration N/A (those aregenerate.*codesowned by the v0.20 command build).
golangci-lint run ./...was attempted but returned findings entirelyinside a different concurrent worktree's vendored-Go-authors code
(
internal/diff, unrelated to this change) — a sandbox artifact of thismulti-agent environment, not a finding against this diff. Only the scoped run above
is claimed as verified.
Tier
Tier 2 (docs/testdata + a self-contained internal package, no CLI/MCP surface, no
data-path code).
Test plan
go build ./...golangci-lint run ./cmd/conduit/internal/generate/...go test -race ./cmd/conduit/internal/generate/...make generate && git diff --exit-codenpx markdownlint-cli2 docs/generate-benchmark.mdOpen questions for DeVaris
adding adversarial/prompt-injection-shaped fixtures now, or defer to the v0.20 PR
that actually wires up retries and can exercise them live?
capability.go's tag→processor map is hand-maintained (not structurally linked tobuiltin.DefaultBuiltinProcessors, to avoid pulling plugin-runtime deps into aYAML-only package) — acceptable for a design-only PR, but flagging so it doesn't
silently drift if builtin processors are renamed before v0.20 lands.
🤖 Generated with Claude Code
https://claude.ai/code/session_015GQFzakPShAYj8CcwajYDD