feat(generate): in-memory validate seam + provider resolution (WS1 seams) - #2772
Merged
Conversation
…ams) Two prerequisites the `conduit generate` design doc names before the command itself can be built. Both are fully testable without any LLM, which is why they come first. 1. validate.RunBytes — the in-memory seam (design §3, "the disk seam") `generate` must gate its candidate through THIS engine, not a second validator, and the candidate exists only in memory. `validate.Run`/ `RunWithOptions` take a path and resolve from disk, so today a candidate could only be validated by writing it out first — which would mean a config that FAILS validation still touched the filesystem, and would put a disk error in the path of a purely in-memory operation. validateFile already opened the path into an io.Reader and did nothing else with the path, so the two paths now share validateReader rather than the in-memory path re-implementing (and drifting from) the parse -> enrich -> validate ordering. RunBytes returns no error, unlike Run: Run's only hard failure is resolving path, and there is no path. Every parse and validation problem comes back as a Finding, exactly as it does for a file that exists but is unparseable. 2. provider — the LLM seam and its resolution rules (design §1) A two-method interface plus deterministic selection. No new dependencies: the adapters (landing next) reuse the already-vendored openai client and follow the `ollama` processor's existing hand-rolled net/http pattern. The load-bearing rule is that there is NO DEFAULT VENDOR. Conduit's broker-neutrality principle extends to model vendors, so with several providers configured and no explicit selection, Resolve REFUSES rather than picking. A silent pick would be hidden favoritism, and it would make a real support question — "why did it use provider X?" — return a wrong answer every time the ordering changed. Two seams exist so resolution never touches real process state or the network: Env for lookups (tests stay parallel-safe and cannot leak into each other) and Probe for Ollama reachability. A nil Probe means "we did not look", and is treated as not-a-candidate — the safe direction, since it can only cause a fixable no_provider_configured error, never a silent wrong pick. Reachability gates candidacy for ollama but not for the hosted providers: a stale OLLAMA_HOST pointing at nothing would otherwise make every invocation ambiguous on a laptop that also has an API key, which is the single most likely misconfiguration. Error codes are conduiterr.Register'ed, so they reach the error-code reference, llms.txt, and the UI. Note for review: §8's taxonomy has no code for an explicit --provider naming something unknown, so that case uses the foundational common.invalid_argument rather than inventing an unlisted `generate.*` code outside the document that defines the contract. If a dedicated code is wanted it belongs in §8 first. Tests, each mutation-verified: - RunBytes: dropping the duplicate-ID pass, hardwiring Options, or dropping the empty-name default each kill their test. The main test asserts RunBytes and disk-Run produce identical findings (code, path, severity — not just counts) across five input shapes, so the two cannot drift. - provider: silently picking the first candidate, probing on an explicit selection, treating a nil probe as reachable, dropping TrimSpace, and switching to alphabetical ordering each kill their test. Risk tier: 3. New leaf package plus an additive, non-breaking function on an internal CLI package. No data path, no serialized format, no new dependencies. Roadmap: v0.20 WS1 (`conduit generate`), design doc §1 and §3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GQFzakPShAYj8CcwajYDD
devarismeroxa
force-pushed
the
feat/ws1-generate-seams
branch
from
August 5, 2026 21:50
073dd6a to
359777e
Compare
Registering two new conduiterr codes is not enough on its own — they have to reach the generated docs. TestAllCodesComplete caught this: reasons registered in source but not visible through allcodes barrel (add the declaring package to allcodes.go's blank-import list): [generate.ambiguous_provider_configuration generate.no_provider_configured] The registry is the single source of truth for the error-code reference, llms.txt, and the UI, but it only sees a code once the declaring package's init has run — which is exactly what the barrel's blank-import list is for. Adds the provider package to that list and regenerates llms.txt/llms-full.txt, per the docs-move-with-code rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GQFzakPShAYj8CcwajYDD
This was referenced Aug 5, 2026
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.
Two prerequisites the
conduit generatedesign doc names before the command itself can be built. Both are fully testable without any LLM, which is why they come first.1.
validate.RunBytes— the in-memory seam (§3, "the disk seam")generatemust gate its candidate through this engine, not a second validator, and the candidate exists only in memory.validate.Runtakes a path and resolves from disk, so today a candidate could only be validated by writing it out first — meaning a config that fails validation still touched the filesystem, and a disk error sat in the path of a purely in-memory operation.validateFilealready opened the path into anio.Readerand did nothing else with the path, so both paths now sharevalidateReaderrather than the in-memory path re-implementing (and drifting from) the parse → enrich → validate ordering.RunBytesreturns no error, unlikeRun:Run's only hard failure is resolvingpath, and there is no path. Everything else comes back as aFinding, exactly as it does for a file that exists but is unparseable.2.
provider— the LLM seam and its resolution rules (§1)A two-method interface plus deterministic selection. No new dependencies — the adapters (landing next) reuse the already-vendored
go-openaiclient and follow theollamaprocessor's existing hand-rollednet/httppattern.The load-bearing rule: there is no default vendor. Conduit's broker-neutrality principle extends to model vendors, so with several configured and no explicit selection,
Resolverefuses rather than picking. A silent pick is hidden favoritism, and it makes a real support question — "why did it use provider X?" — return a wrong answer every time the ordering changes.Two seams keep resolution off real process state and off the network:
EnvProbeno_provider_configured, never a silent wrong pickReachability gates candidacy for
ollamabut not for hosted providers: a staleOLLAMA_HOSTpointing at nothing would otherwise make every invocation ambiguous on a laptop that also has an API key — the single most likely misconfiguration.Flagged for your decision
§8's error taxonomy has no code for an explicit
--providernaming something unknown. I used the foundationalcommon.invalid_argumentrather than inventing an unlistedgenerate.*code outside the document that defines the contract. If you want a dedicated code, it belongs in §8 first, then here.Adversarial self-review
git add -Aswept theRunByteswork into the unrelated staticcheck PR. Caught it, rebuilt fix(test): use t.Fatalf so staticcheck sees the nil check terminates #2771 as a clean single-file change, and moved this here. fix(test): use t.Fatalf so staticcheck sees the nil check terminates #2771 is now 13 lines in one file.rm -rf'dcmd/conduit/internal/generate/— which already existed onmain(the v0.19 eval harness). Verified it never reached a commit and restored it;git statusclean, package intact.Candidatesorder — sorted by the fixedNamesorder, not alphabetically. The two coincide today; pinning it means a future provider rename can't silently reorder every error message and doctor line.--provider " "must not be an "unknown provider" error.TrimSpacebefore the validity check, so an emptyCONDUIT_GENERATE_PROVIDER=""in CI falls through to auto-detect.Tests — every mutation verified
RefusesToPickAVendorExplicitSkipsProbingNilProbeNeverAutoDetectsOllama+ 2 moreTrimSpaceon explicit valuesBlankValuesAreNotSelectionsDeterministicOrderRunBytes' duplicate-ID passMatchesRunOnDiskOptions{}inRunBytesHonoursOptionsDefaultsTheNameMatchesRunOnDiskcompares the findings themselves — code, config path, severity — not just counts, across five input shapes (valid, unparseable, missing required fields, empty, duplicate IDs). A seam returning the right number of different findings would be worse than one returning none.Risk tier
3. New leaf package plus an additive, non-breaking function on an internal CLI package. No data path, no serialized format, no new dependencies.
Roadmap
v0.20 WS1 (
conduit generate), design doc §1 and §3. Stacks on #2769 (fuzzymatch, §7). Next: the three provider adapters and the generation loop.🤖 Generated with Claude Code
https://claude.ai/code/session_015GQFzakPShAYj8CcwajYDD