os-me: eval adapter choice moves to one shared object; row_labels gains --adapter #1291
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1291
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Jev beat Sonnet on the ReplyShape holdout split: 88.0% against 85.3%, 4.6 s against 53 s, $0.0042 per run. The S10 lever evals must now run on Jev. They cannot do that today.
The adapter choice lives only inside
plugins/os-me/eval/reply_shape/run.rb:ADAPTERS = %w[headless ollama jev], ajev_adaptermethod that readsCF_ACCOUNT_IDandCF_API_TOKENwithENV.fetch, and abuild_adaptercase.plugins/os-me/eval/row_labels/run.rb(S10 T2, #1285) has no--adapterflag. Itsbuild_clientalways buildsOsMe::HeadlessClaude, and its--modeldefault isOsMe::MomentExtractor::LABELER_MODEL('sonnet').Two more facts:
--modelgoes toHeadlessClaude(withLABELER_MODELas the fallback) and toSystemOne.ollama(gem defaulttev1:4b). The script ignores--modelfor jev, so the jev adapter always uses the gem defaulttypesafe/jev.KeyErrorfromrun_all, outside thebegin/rescue. The script then stops with a backtrace.Approach
Add one lib object,
OsMe::EvalAdapter, that maps an adapter name to a builtDecision::Modeladapter. Both eval scripts use it. The jev branch reads the two CF variables through an injectableenv:(defaultENV). If a variable is missing,buildraisesOsMe::Errorwith the variable name and never the value. An unknown name raisesOsMe::Errorwith the list of names.reply_shape keeps its flags, its validation message, its TSV, and its summary and cost lines. It only takes the name list and the adapter from the new object.
row_labels gets
--adapter VALUE(defaultheadless, so a run without the flag works as before). Its--modeldefault becomesnil, andEvalAdapterappliesLABELER_MODELonly on the headless branch. If the default stayed'sonnet', ollama would get the wrong model. row_labels prints no cost line today, andLeverResultdoes not carry usage, so this ticket adds no cost line (see Scope).The tests load
EvalAdapterfromlib/and do not toucheval/. The worktree guard blocks shell paths that contain "eval". The two script-level criteria (7 and 8) extend the existingtest/row_labels_run_test.rb, which already gets the script path from Ruby (File.expand_path('../eval/row_labels/run.rb', __dir__)) and starts it withOpen3, not with a shell command line.Required work:
OsMe::EvalAdapterwithNAMESand.build(name, model: nil, env: ENV)as in the Owner sketch. check: presentNAMES = %w[headless ollama jev].freezeinplugins/os-me/lib/os_me/eval_adapter.rb.build, raiseOsMe::Errorfor an unknown name and list the names. check: presentunknown adapterinplugins/os-me/lib/os_me/eval_adapter.rbADAPTERS,jev_adapter, andbuild_adapter. check: absentdef jev_adapterinplugins/os-me/eval/reply_shape/run.rbvalidate_choicesusesOsMe::EvalAdapter::NAMESfor--adapter. check: presentadapter: OsMe::EvalAdapter::NAMESinplugins/os-me/eval/reply_shape/run.rbrun_allgets its adapter fromOsMe::EvalAdapter.build. check: presentOsMe::EvalAdapter.build(options[:adapter], model: options[:model])inplugins/os-me/eval/reply_shape/run.rbadapter: niltoFLAGS, set the defaults toadapter: 'headless', model: nil, and build the client fromEvalAdapter. check: presentOsMe::EvalAdapter.build(options[:adapter], model: options[:model])inplugins/os-me/eval/row_labels/run.rb--modeldefaultLABELER_MODEL. check: absentmodel: OsMe::MomentExtractor::LABELER_MODELinplugins/os-me/eval/row_labels/run.rbEvalAdaptertests (criteria 1-6) and extend the row_labels script test (criteria 7-8). check: presentclass EvalAdapterTestinplugins/os-me/test/eval_adapter_test.rbAcceptance criteria
OsMe::EvalAdapter.build('headless')runs, then the result is anOsMe::HeadlessClaudewhosenameisheadless:sonnet.model: 'haiku', whenOsMe::EvalAdapter.build('headless', model: 'haiku')runs, then the result'snameisheadless:haiku.model: 'tev1:4b', whenOsMe::EvalAdapter.build('ollama', model: 'tev1:4b')runs, then the result is aDecision::Model::Adapters::SystemOnewhosenameisollama:tev1:4b, and the test sends no request.env: { 'CF_ACCOUNT_ID' => 'fake-account', 'CF_API_TOKEN' => 'fake-token' }andmodel: 'sonnet', whenOsMe::EvalAdapter.build('jev', model: 'sonnet', env:)runs, then the result is aDecision::Model::Adapters::CloudflareJevwhosenameiscloudflare:typesafe/jev. The jev adapter ignores the model, as reply_shape does today.env: { 'CF_ACCOUNT_ID' => 'fake-account' }(no token), whenbuild('jev', env:)runs, then it raisesOsMe::Error. The message containsCF_API_TOKENand does not containfake-account.bogus, whenOsMe::EvalAdapter.build('bogus')runs, then it raisesOsMe::Error. The message containsbogusandheadless, ollama, jev.--adapter bogus, then it exits 2, and stderr starts witherror:and containsheadless, ollama, jev.CF_ACCOUNT_IDandCF_API_TOKENunset (anOpen3.capture3env hash with nil values), when row_labels runs with--adapter jev, then it exits 2, stderr containsCF_ACCOUNT_ID, and the script makes no model call.Owner sketch
Scope and blocking
plugins/os-me/lib/os_me/eval_adapter.rb(new),plugins/os-me/eval/reply_shape/run.rb,plugins/os-me/eval/row_labels/run.rb,plugins/os-me/test/eval_adapter_test.rb(new),plugins/os-me/test/row_labels_run_test.rb.LeverRun/LeverResultdo not carry usage, so ajev_cost_usdline needs usage plumbing throughLeverCheckfirst. Also out of scope: moving reply_shape's adapter build into itsbegin/rescue, a--modelpass-through for jev, andRowLabels.build, which stays on headless.env:and do not change the realENV.--adapter) are merged.Origin
CODE(plugins/os-me/eval/reply_shape/run.rb).VERDICT: CREATE (user-approved; backlog search only)
Work started on branch ticket-1291 (os-sdlc implement).
Resolution
Done: OsMe::EvalAdapter owns adapter selection by name; reply_shape and row_labels both use it; row_labels gains --adapter headless|ollama|jev
Evidence: Implementation 153 complete in 4 min 23 s, 8 dispatches, all pass; suite gate green
Follow-ups: row_labels cost line needs usage carried through LeverRun (deferred, not filed: no current need); one LENS line (A6, ENV default) goes to the lens challenge