os-me: move HeadlessRunner onto llm-stack #1438
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#1438
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
OsMe::HeadlessRunnerinplugins/os-me/lib/os_me/headless_runner.rbbuilds aclaude -pargv, runs it with Open3, and parses the JSON reply for text, tokens, and cost.It has no timeout today.
ADR-0301 puts this work in the
llm-stackgem.Was blocked by #1437, merged 2026-10-27 at
0faed2ff.Reproduce
n/a: this is a feature, not a defect.
Approach
Required work:
gem "llm-stack", git: "ssh://git@forgejo.swansoncloud.com:2222/jared/llm-stack.git", require: "llm"toplugins/os-me/Gemfileand commitplugins/os-me/Gemfile.lock(ADR-0166) — check: presentllm-stackinplugins/os-me/Gemfile.lockHeadlessRunner#runwithLLM::Client#chaton aclaude_codeprofile; build the profile settings from theHeadlessRequestfieldscwd,model,tools, andread_roots(gem settingscwd,allowed_tools,read_roots), and set a timeout, which the runner lacks today — check: absentOpen3inplugins/os-me/lib/os_me/headless_runner.rbHeadlessResultfrom the gemResponse:text,usagefor the token counts, andcostforcost_usd; keepstructured_outputfor the schema path — check: presentResponseinplugins/os-me/lib/os_me/headless_runner.rbHeadlessRunner#parseandHeadlessRunner#input_tokens, and the flag builders inHeadlessRequest#argv(allowed_flags,model_flags,structured_flags,root_flags), because the gem now owns the CLI flags and the reply parse — check: absentstructured_flagsinplugins/os-me/lib/os_me/headless_request.rbHeadlessRequest#allow_rulesstill builds theTool(/root/**)rules, and the profile'sallowed_toolssetting carries them — check: presentallow_rulesinplugins/os-me/lib/os_me/headless_request.rbplugins/os-me/test/headless_runner_test.rbandplugins/os-me/test/test_helper.rbto use a fakeclaudescript through the profileexecutablesetting; no test runs the real binary — check: absentcommand(prompt:inplugins/os-me/test/headless_runner_test.rbOrchestrator work: run
bin/refresh-pluginsand add one History row to the os-me leaf underdocs/implementation-status/.Acceptance criteria
claudethat returns text, usage, and cost, whenHeadlessRunner#callruns, then theHeadlessResultholds the same text, token counts, and cost.schema:argument, whenHeadlessRunner#structuredruns against a fakeclaudethat returns structured output, then the result holds that structured output.claudethat never answers, whenHeadlessRunner#callruns, then it raises after the timeout.claudethat reportsis_error, whenHeadlessRunner#callruns, then it raises with the reported message.toolsandread_roots, whenHeadlessRunner#callruns, then the fakeclauderecords the same allow rules and--add-dirvalues that the old command built.Owner sketch
Non-goals
plugins/os-me/lib/os_me/headless_claude.rb: it moves in spec section 6 step 5.HeadlessResultfields or to the callers ofHeadlessRunner.claudebinary in any test.Origin
VERDICT: CREATE
REASON: Human-ruled work from ADR-0301 and spec section 3.4; no open ticket with the same mechanism.
Work started on branch ticket-1438 via /os-sdlc:implement (session 375f16d0). Body checks corrected to the current HeadlessRequest/HeadlessRunner split before open.
Resolution
Done: HeadlessRunner calls LLM::Client#chat on a claude_code profile built by HeadlessRequest#profile; HeadlessResult comes from the gem Response; Open3 and the argv builders are gone; plugins/os-me Gemfile and lockfile pin llm-stack 87fc9a4.
Evidence: Commit
271555afmerged to main atf5c043a5. os-me suite 442 runs, 808 assertions, 0 failures. Root rubocop clean on changed files. Design audit (contract-auditor, opus): no fix-required findings. Gem change 87fc9a4 omits --model when the profile has none.Follow-ups: none