os-adr: move Adr::Judge::ClaudeCommand onto the llm-stack gem #1437
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#1437
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
Adr::Judge::ClaudeCommandinplugins/os-adr/lib/adr/judge/claude_command.rbshells out toclaude -pwith its own Open3 call, argv list, and timeout.Several cc-os plugins repeat this shell-out.
ADR-0301 makes the
llm-stackgem the one place that wraps theclaudebinary.This ticket is the first caller to move (spec section 6, step 2).
It proves the gem against a real caller before the other plugins follow.
plugins/os-adrhas no Gemfile today, so the ticket also adds one.Reproduce
n/a: this is a feature, not a defect.
Approach
Required work:
plugins/os-adr/Gemfilewithgem "llm-stack", git: "ssh://git@forgejo.swansoncloud.com:2222/jared/llm-stack.git", require: "llm", pluszeitwerkandminitestin the test group, and commitplugins/os-adr/Gemfile.lock(ADR-0166) — check: presentllm-stackinplugins/os-adr/Gemfile.lockplugins/os-adr/lib/adr/judge/claude_command.rband addplugins/os-adr/lib/adr/judge/model_chat.rbwith classAdr::Judge::ModelChat— check: absentOpen3inplugins/os-adr/lib/adr/judge/*.rbModelChatbuild its ownLLM::Clientwith one profile of providerclaude_code, the judge model, and these settings:cwdis the repo root,toolsis empty,budget_usdis 0.5,timeoutis 180 — check: presentbudget_usdinplugins/os-adr/lib/adr/judge/model_chat.rbModelChat#run(prompt)callclient.chat(profile:, prompt:, system: card, schema:)and returnResponse#text, which is the verdict JSON text — check: presentschema:inplugins/os-adr/lib/adr/judge/model_chat.rbAdr::Judgeto takechat:in place ofrunner:, to callJSON.parseon the text, and to pass the hash toVerdict.parse; remove thereply['is_error']andstructured_outputenvelope handling, because the gem now owns the envelope — check: absentstructured_outputinplugins/os-adr/lib/adr/judge.rbAdr::Judge::Unavailableand keep the meaning of the three old messages:LLM::TimedOutgives "claude did not answer within 180 seconds";LLM::ProviderErrorgives "claude failed: " plus the first 200 characters of the error message, which also covers a missing executable and a non-zero exit; unparsable text gives "claude reply is not JSON: " plus the first 200 characters — check: presentLLM::TimedOutinplugins/os-adr/lib/adr/judge/model_chat.rbplugins/os-adr/tests/judge_test.rbto use a fakeclaudescript, set through the profileexecutablesetting, in the style oftest/support/bin/claudein the llm-stack gem; no test runs the real binary — check: absentRecordingRunnerinplugins/os-adr/tests/judge_test.rbplugins/os-adr/bin/*scripts load the bundle beforelib/adr— check: presentbundler/setupinplugins/os-adr/bin/*Orchestrator work: run
bin/refresh-plugins, then update the os-adr and llm-stack leaves underdocs/implementation-status/with one History row each, and mark spec section 6 step 2 as migrated.Acceptance criteria
claudethat returns a valid verdict, whenAdr::Judge#callruns increatemode, then it returns aVerdictwith the fake's verdict.claudethat sleeps past the timeout, whenAdr::Judge#callruns, then it raisesUnavailablewith the 180 second message.claudethat exits non-zero, whenAdr::Judge#callruns, then it raisesUnavailablethat starts with "claude failed".executablesetting that names no file, whenAdr::Judge#callruns, then it raisesUnavailable.claudethat prints text that is not JSON, whenAdr::Judge#callruns, then it raisesUnavailablewith "claude reply is not JSON".clauderecords its argv, whenAdr::Judge#callruns, then the argv holds the model, an empty tool list, the card as the system prompt, the schema, and a 0.5 dollar budget cap, and the prompt arrives on stdin.plugins/os-adr/libforOpen3, then nothing matches.Owner sketch
Non-goals
claude -p: they have their own tickets.Verdictclass.LLM.configurecall in the plugin.claudebinary in any test.Origin
VERDICT: CREATE
REASON: First migration in the human-ruled ADR-0301 / spec section 6 plan; no open ticket covers it.
Work started on branch ticket-1437 via /os-sdlc:implement (session 375f16d0).
Resolution
Done: Adr::Judge::ModelChat calls the llm-stack gem's LLM::Client#chat on a claude_code profile; ClaudeCommand and Open3 removed; plugins/os-adr has a Gemfile and lockfile pinned to llm-stack from the Forgejo git source; Judge takes a chat: seam.
Evidence: Commit
3ef516f5merged to main at0faed2ff. os-adr suite 114 runs, 365 assertions, 0 failures. Root rubocop clean on changed files. Design audit (contract-auditor, opus): one fix-required (bin require order) applied; rest answered acceptable. bin/adr-find and bin/adr-detect run under the new Gemfile.Follow-ups: Dropped: a shared contract test between CannedChat and ModelChat. Reason: the judge argv tests already drive the real ModelChat end to end against a fake claude executable, so the double is pinned by the same acceptance behavior. No other follow-ups.