Migrate cc-os plugins to the RubyLLM-native llm-stack (llm-stack ADR 0004) #1502

Closed
opened 2026-11-01 17:22:21 +00:00 by jared · 5 comments
Owner

Problem

llm-stack ticket #19 rebuilds llm-stack as a RubyLLM extension (ADR 0004).
The rebuild is on llm-stack branch rebuild/rubyllm-extension. The deletion commit is c8891f0.
The rebuild removes LLM::Client, LLM::Response, LLM::Decision, LLM::Usage, LLM::Embedding, LLM::PriceTable, LLM::TimedOut, LLM::ProviderError and LLM::Http::Errors.
Three cc-os plugins call these classes: os-me, os-adr and os-sdlc (ticket-takedown).
The code breaks when it loads the rebuilt gem.
A profile now builds a RubyLLM::Context with profile.context(credentials). The caller gets RubyLLM types and RubyLLM errors.
LLM::Profile.find and from_h keep their names. require "llm" keeps working.
Claude Code is now a RubyLLM provider (claude_code). Profile settings (cwd, tools, executable, timeout) go into the context through the profile.
The llm-stack branch is pushed to origin. The pin style stays a bundler path: source; do not use git: ..., ref:. See the Migration brief comment.
llm-stack merges to main only after both consumers are green. The decision-model ticket is the other consumer. The two tickets do not depend on each other.
An open decision on llm-stack #19 may tighten the ruby_llm pin.
Some Gemfile.lock files in cc-os have uncommitted changes. They are not ours to touch. Do not stage, revert or regenerate them for this work. Use a worktree or stage by explicit path.

Approach

Open a migration branch in cc-os. In the three plugin Gemfiles, change the path: of llm-stack from dev/ruby-gems/llm-stack to dev/ruby-gems/llm-stack-rebuild.
After llm-stack merges to main, change path: back to dev/ruby-gems/llm-stack, run each suite, and merge. Do not merge the migration branch before then.
Source: llm-stack docs/parity/llm-stack.md, section "Consumer call sites". Line numbers are stale (taken from cc-os 496c2184); use rg for exact lines.

file:line today after
plugins/os-me/Gemfile:12; plugins/os-adr/Gemfile:9; plugins/os-sdlc/Gemfile:14 gem 'llm-stack', path: File.expand_path('dev/ruby-gems/llm-stack', Etc.getpwuid.dir), require: 'llm' path: to dev/ruby-gems/llm-stack-rebuild during migration, then back to dev/ruby-gems/llm-stack
plugins/os-me/lib/os_me.rb:6; plugins/os-adr/lib/adr.rb:5; plugins/os-sdlc/eval/ticket-takedown/lib/takedown.rb:5 require "llm" No change.
plugins/os-me/lib/os_me/headless_runner.rb:13,20-21 Client.new(profile: Profile.from_h(:os_me_headless, ...), credentials: {}) then chat(prompt:, system:, schema:) profile.context.chat(**profile.keywords).with_instructions(system).with_schema(schema).ask(prompt)
plugins/os-me/lib/os_me/headless_runner.rb:26-28 response => LLM::Response[text:, usage: {input:, output:, cache_read:, cache_write:}, meta:]; meta.fetch('reported_cost') Read message.content, message.tokens (fields may be nil) and message.cost.total (the CLI reported_cost). meta is gone.
plugins/os-me/lib/os_me/headless_request.rb:11-14 Profile hash with provider: 'claude_code', model, settings: {cwd, tools, allowed_tools, read_roots, executable, timeout} Same keys under LLM::Profile.from_h. Unknown keys now raise LLM::InvalidProfile. Per-call cwd, tools, allowed_tools can use with_provider_options.
plugins/os-me/lib/os_me/ollama_embedder.rb:6,14 Client.new(profile: Profile.find(provider: 'ollama', model: 'nomic-embed-text')); embed(texts:).vectors profile.context.embed(texts, **profile.keywords, provider_options: profile.provider_options).vectors
plugins/os-adr/lib/adr/judge/model_chat.rb:15,19-24,29-31 Client.new(profile:, credentials: {}); chat(prompt:, system:, schema:).text; rescue LLM::TimedOut; rescue LLM::ProviderError => e profile.context.chat(...).ask(prompt).content; rescue RubyLLM::Error => e (covers LLM::TimedOut, LLM::ProcessFailed, LLM::ReplyFailed; these are new RubyLLM::Error subclasses). Keep the 200 character cut of the message.
plugins/os-sdlc/eval/ticket-takedown/lib/takedown/adapter/claude_executor.rb:7,16-19,26,30-32 LLM::PriceTable.new({}) as REPORTED_COSTS; Client.new(profile:, credentials: {}, prices: REPORTED_COSTS); rescue LLM::TimedOut, LLM::ProviderError Delete REPORTED_COSTS and prices:. Use message.cost.total, which is the CLI reported_cost. rescue RubyLLM::Error.
plugins/os-sdlc/eval/ticket-takedown/lib/takedown/parser/claude_reply.rb:13-17,31 response => LLM::Response; response.text, response.cost, meta["raw"], ["num_turns"], ["models"], ["session_id"]; usage.to_h with keys input, cache_write, cache_read, output message.content, message.cost.total. Read session_id, num_turns and models from message.raw. Build the usage hash from message.tokens (fields may be nil).
plugins/os-me/test/headless_runner_chat_test.rb:29,35 assert_raises(LLM::TimedOut), assert_raises(LLM::ProviderError) assert_raises(LLM::TimedOut), assert_raises(LLM::ProcessFailed) (the new RubyLLM::Error subclasses)
plugins/os-me/test/headless_runner_cost_test.rb:16-20 Stubs LLM::Client.new to add prices: LLM::PriceTable.default Remove the stub. Assert on message.cost.total.
plugins/os-me/test/llm_embed_stub.rb:5,21-25; plugins/os-me/test/ollama_embedder_test.rb:26 LLM::Embedding.new(vectors:, usage: LLM::Usage.new, cost: 0.0); stubs Client.new Build RubyLLM::Embedding and stub Context#embed. Profile.find stays.
docs/llm-stack/spec.md Spec of the old client API Amend to describe the RubyLLM-native API (ADR 0004).
docs/adr/0301-*.md, docs/adr/0303-*.md ADRs that name the old client Amend, or supersede, to point to llm-stack ADR 0004.

Required work: none beyond the acceptance criteria.

Acceptance criteria

  1. Given cc-os with llm-stack path: pointed at dev/ruby-gems/llm-stack-rebuild in os-me, os-adr and os-sdlc, when each plugin test suite runs, then every suite is green.
  2. Given the cc-os source tree, when you search for LLM::Client, LLM::Response, LLM::Decision, LLM::Usage, LLM::Http::Errors, LLM::TimedOut, LLM::ProviderError and LLM::PriceTable, then no reference to the removed classes remains in code, tests or docs, except where an ADR records history. LLM::TimedOut is a new RubyLLM::Error subclass in the rebuild, so a hit that rescues or asserts the new class is allowed.
  3. Given a Claude Code call in os-me, os-adr or the os-sdlc takedown, when the CLI times out or fails, then the caller rescues RubyLLM::Error and handles it as it did before.
  4. Given a takedown reply, when claude_reply.rb parses it, then it reports the CLI cost from message.cost.total and reads session id, turn count and models from message.raw.
  5. Given the os-me embedder, when it embeds texts, then it returns vectors from RubyLLM::Embedding#vectors in input order.
  6. Given docs/llm-stack/spec.md and ADRs 0301 and 0303, when a reader opens them, then they describe the RubyLLM-native API and point to llm-stack ADR 0004.
  7. Given the llm-stack rebuild is merged to main, when each Gemfile path: points back at dev/ruby-gems/llm-stack, then every suite is still green.
  8. Given the diff of this migration, when you review it, then it contains no change to the uncommitted Gemfile.lock edits that exist in cc-os today, and no change outside cc-os.

Discoverer: /home/jared/dev/ruby-gems/llm-stack, session 4ee5ef17-d652-472d-853c-91dd2af699ea, 2026-11-01. llm-stack removed LLM::Client and its wrappers (ADR 0004); this repo calls them.

## Problem llm-stack ticket #19 rebuilds llm-stack as a RubyLLM extension (ADR 0004). The rebuild is on llm-stack branch `rebuild/rubyllm-extension`. The deletion commit is c8891f0. The rebuild removes LLM::Client, LLM::Response, LLM::Decision, LLM::Usage, LLM::Embedding, LLM::PriceTable, LLM::TimedOut, LLM::ProviderError and LLM::Http::Errors. Three cc-os plugins call these classes: os-me, os-adr and os-sdlc (ticket-takedown). The code breaks when it loads the rebuilt gem. A profile now builds a `RubyLLM::Context` with `profile.context(credentials)`. The caller gets RubyLLM types and RubyLLM errors. `LLM::Profile.find` and `from_h` keep their names. `require "llm"` keeps working. Claude Code is now a RubyLLM provider (`claude_code`). Profile settings (`cwd`, `tools`, `executable`, `timeout`) go into the context through the profile. The llm-stack branch is pushed to origin. The pin style stays a bundler `path:` source; do not use `git: ..., ref:`. See the Migration brief comment. llm-stack merges to `main` only after both consumers are green. The decision-model ticket is the other consumer. The two tickets do not depend on each other. An open decision on llm-stack #19 may tighten the ruby_llm pin. Some `Gemfile.lock` files in cc-os have uncommitted changes. They are not ours to touch. Do not stage, revert or regenerate them for this work. Use a worktree or stage by explicit path. ## Approach Open a migration branch in cc-os. In the three plugin Gemfiles, change the `path:` of llm-stack from `dev/ruby-gems/llm-stack` to `dev/ruby-gems/llm-stack-rebuild`. After llm-stack merges to `main`, change `path:` back to `dev/ruby-gems/llm-stack`, run each suite, and merge. Do not merge the migration branch before then. Source: llm-stack `docs/parity/llm-stack.md`, section "Consumer call sites". Line numbers are stale (taken from cc-os 496c2184); use `rg` for exact lines. | file:line | today | after | |---|---|---| | `plugins/os-me/Gemfile:12`; `plugins/os-adr/Gemfile:9`; `plugins/os-sdlc/Gemfile:14` | `gem 'llm-stack', path: File.expand_path('dev/ruby-gems/llm-stack', Etc.getpwuid.dir), require: 'llm'` | `path:` to `dev/ruby-gems/llm-stack-rebuild` during migration, then back to `dev/ruby-gems/llm-stack` | | `plugins/os-me/lib/os_me.rb:6`; `plugins/os-adr/lib/adr.rb:5`; `plugins/os-sdlc/eval/ticket-takedown/lib/takedown.rb:5` | `require "llm"` | No change. | | `plugins/os-me/lib/os_me/headless_runner.rb:13,20-21` | `Client.new(profile: Profile.from_h(:os_me_headless, ...), credentials: {})` then `chat(prompt:, system:, schema:)` | `profile.context.chat(**profile.keywords).with_instructions(system).with_schema(schema).ask(prompt)` | | `plugins/os-me/lib/os_me/headless_runner.rb:26-28` | `response => LLM::Response[text:, usage: {input:, output:, cache_read:, cache_write:}, meta:]`; `meta.fetch('reported_cost')` | Read `message.content`, `message.tokens` (fields may be nil) and `message.cost.total` (the CLI `reported_cost`). `meta` is gone. | | `plugins/os-me/lib/os_me/headless_request.rb:11-14` | Profile hash with `provider: 'claude_code'`, `model`, `settings: {cwd, tools, allowed_tools, read_roots, executable, timeout}` | Same keys under `LLM::Profile.from_h`. Unknown keys now raise `LLM::InvalidProfile`. Per-call `cwd`, `tools`, `allowed_tools` can use `with_provider_options`. | | `plugins/os-me/lib/os_me/ollama_embedder.rb:6,14` | `Client.new(profile: Profile.find(provider: 'ollama', model: 'nomic-embed-text'))`; `embed(texts:).vectors` | `profile.context.embed(texts, **profile.keywords, provider_options: profile.provider_options).vectors` | | `plugins/os-adr/lib/adr/judge/model_chat.rb:15,19-24,29-31` | `Client.new(profile:, credentials: {})`; `chat(prompt:, system:, schema:).text`; `rescue LLM::TimedOut`; `rescue LLM::ProviderError => e` | `profile.context.chat(...).ask(prompt).content`; `rescue RubyLLM::Error => e` (covers `LLM::TimedOut`, `LLM::ProcessFailed`, `LLM::ReplyFailed`; these are new `RubyLLM::Error` subclasses). Keep the 200 character cut of the message. | | `plugins/os-sdlc/eval/ticket-takedown/lib/takedown/adapter/claude_executor.rb:7,16-19,26,30-32` | `LLM::PriceTable.new({})` as `REPORTED_COSTS`; `Client.new(profile:, credentials: {}, prices: REPORTED_COSTS)`; `rescue LLM::TimedOut, LLM::ProviderError` | Delete `REPORTED_COSTS` and `prices:`. Use `message.cost.total`, which is the CLI `reported_cost`. `rescue RubyLLM::Error`. | | `plugins/os-sdlc/eval/ticket-takedown/lib/takedown/parser/claude_reply.rb:13-17,31` | `response => LLM::Response`; `response.text`, `response.cost`, `meta["raw"]`, `["num_turns"]`, `["models"]`, `["session_id"]`; `usage.to_h` with keys `input, cache_write, cache_read, output` | `message.content`, `message.cost.total`. Read `session_id`, `num_turns` and `models` from `message.raw`. Build the usage hash from `message.tokens` (fields may be nil). | | `plugins/os-me/test/headless_runner_chat_test.rb:29,35` | `assert_raises(LLM::TimedOut)`, `assert_raises(LLM::ProviderError)` | `assert_raises(LLM::TimedOut)`, `assert_raises(LLM::ProcessFailed)` (the new `RubyLLM::Error` subclasses) | | `plugins/os-me/test/headless_runner_cost_test.rb:16-20` | Stubs `LLM::Client.new` to add `prices: LLM::PriceTable.default` | Remove the stub. Assert on `message.cost.total`. | | `plugins/os-me/test/llm_embed_stub.rb:5,21-25`; `plugins/os-me/test/ollama_embedder_test.rb:26` | `LLM::Embedding.new(vectors:, usage: LLM::Usage.new, cost: 0.0)`; stubs `Client.new` | Build `RubyLLM::Embedding` and stub `Context#embed`. `Profile.find` stays. | | `docs/llm-stack/spec.md` | Spec of the old client API | Amend to describe the RubyLLM-native API (ADR 0004). | | `docs/adr/0301-*.md`, `docs/adr/0303-*.md` | ADRs that name the old client | Amend, or supersede, to point to llm-stack ADR 0004. | Required work: none beyond the acceptance criteria. ## Acceptance criteria 1. Given cc-os with llm-stack `path:` pointed at `dev/ruby-gems/llm-stack-rebuild` in os-me, os-adr and os-sdlc, when each plugin test suite runs, then every suite is green. 1. Given the cc-os source tree, when you search for LLM::Client, LLM::Response, LLM::Decision, LLM::Usage, LLM::Http::Errors, LLM::TimedOut, LLM::ProviderError and LLM::PriceTable, then no reference to the removed classes remains in code, tests or docs, except where an ADR records history. `LLM::TimedOut` is a new `RubyLLM::Error` subclass in the rebuild, so a hit that rescues or asserts the new class is allowed. 1. Given a Claude Code call in os-me, os-adr or the os-sdlc takedown, when the CLI times out or fails, then the caller rescues `RubyLLM::Error` and handles it as it did before. 1. Given a takedown reply, when `claude_reply.rb` parses it, then it reports the CLI cost from `message.cost.total` and reads session id, turn count and models from `message.raw`. 1. Given the os-me embedder, when it embeds texts, then it returns vectors from `RubyLLM::Embedding#vectors` in input order. 1. Given `docs/llm-stack/spec.md` and ADRs 0301 and 0303, when a reader opens them, then they describe the RubyLLM-native API and point to llm-stack ADR 0004. 1. Given the llm-stack rebuild is merged to `main`, when each Gemfile `path:` points back at `dev/ruby-gems/llm-stack`, then every suite is still green. 1. Given the diff of this migration, when you review it, then it contains no change to the uncommitted `Gemfile.lock` edits that exist in cc-os today, and no change outside cc-os. -------- **Discoverer:** /home/jared/dev/ruby-gems/llm-stack, session 4ee5ef17-d652-472d-853c-91dd2af699ea, 2026-11-01. llm-stack removed LLM::Client and its wrappers (ADR 0004); this repo calls them.
Author
Owner

Migration brief (2026-11-02)

Self-contained start sheet for cc-os #1502. Read this, then the body.

Body edits made today

  • Replaced git: ..., ref: <sha> pinning with the bundler path: flow (user decision). The Gemfile row now names the real current lines: plugins/os-me/Gemfile:12, plugins/os-adr/Gemfile:9, plugins/os-sdlc/Gemfile:14 (the body said 10, 7, 12), and the real path: text.
  • Said the llm-stack branch is pushed.
  • Fixed the Claude Code error names. The body said LLM::ClaudeCode::TimedOut and ProcessFailed. The rebuild has top-level LLM::TimedOut, LLM::ProcessFailed, LLM::ReplyFailed (all < RubyLLM::Error). There is no LLM::ClaudeCode:: error namespace.
  • Acceptance criterion 2: LLM::TimedOut is a NEW rebuild class with an old name, so a use of the new class is allowed.
  • Rewrote the pin criteria (1 and 7) for the path: flow.

State of llm-stack

Branch rebuild/rubyllm-extension, pushed to origin (forgejo jared/llm-stack). Local worktree: ~/dev/ruby-gems/llm-stack-rebuild. Its tests pass. Live smokes passed on 2026-11-02 (claude_code subscription, Cloudflare jev judge, Ollama).

Flow

  1. Use a worktree or branch in cc-os (some Gemfile.lock files have uncommitted edits; do not stage, revert or regenerate them; stage by explicit path). Change path: in plugins/os-me/Gemfile:12, plugins/os-adr/Gemfile:9 and plugins/os-sdlc/Gemfile:14 from dev/ruby-gems/llm-stack to dev/ruby-gems/llm-stack-rebuild.
  2. Migrate the call sites. Get all three suites green.
  3. Do NOT merge. Report to Jared that the branch is green. llm-stack then merges the rebuild to main.
  4. Change the three path: values back to dev/ruby-gems/llm-stack, run the suites again, then merge.

API reference (rebuild worktree; confirm names there)

README.md, docs/design.md, docs/adr/0004*, docs/parity/llm-stack.md ("Consumer call sites"), docs/parity/rebuild-plan.md section 4.

  • Removed: LLM::Client, LLM::Http::Errors::*, LLM::PriceTable (use message.cost.total), LLM::Response, LLM::Decision, LLM::Usage, LLM::Embedding, LLM::ProviderError.
  • Kept: LLM::Profile.find/from_h, LLM::InvalidProfile, LLM::Error (StandardError; gem configuration errors only).
  • New, all < RubyLLM::Error: LLM::InvalidJudgment, LLM::BadReply, LLM::TimedOut, LLM::ProcessFailed, LLM::ReplyFailed. Callers rescue RubyLLM::Error (plus Faraday::TimeoutError, Faraday::ConnectionFailed, LLM::Error where needed).
  • ruby_llm pinned exactly at 2.1.1. claude_code models: haiku, sonnet, opus. cwd is required for claude_code.

Call sites that still use the removed API (rg, 2026-11-02, cc-os 268d6a9e; paths verified)

os-me:

  • plugins/os-me/lib/os_me/headless_runner.rb:20 (LLM::Client.new), :26 (LLM::Response)
  • plugins/os-me/lib/os_me/ollama_embedder.rb:6
  • plugins/os-me/test/llm_embed_stub.rb:5 (LLM::Embedding, LLM::Usage), :21-25 (LLM::Client)
  • plugins/os-me/test/headless_runner_cost_test.rb:16-20 (LLM::Client, PriceTable)
  • plugins/os-me/test/headless_runner_chat_test.rb:29 (LLM::TimedOut: old class), :35 (LLM::ProviderError)
    os-adr:
  • plugins/os-adr/lib/adr/judge/model_chat.rb:15 (LLM::Client), :20 (LLM::TimedOut), :22 (LLM::ProviderError)
    os-sdlc:
  • plugins/os-sdlc/eval/ticket-takedown/lib/takedown/adapter/claude_executor.rb:7 (PriceTable), :19 (TimedOut, ProviderError), :26 (Client)
  • plugins/os-sdlc/eval/ticket-takedown/lib/takedown/parser/claude_reply.rb:13 (LLM::Response)
    Docs: docs/llm-stack/spec.md, docs/adr/0301-llm-stack-gem-is-the-shared-llm-layer-pinned-per-plugin-from-forgejo.md, docs/adr/0303-llm-stack-gem-is-the-shared-llm-layer-pinned-per-plugin-from-forgejo.md (both ADR titles are the same; read both). Also docs/implementation-status/os-me.md and os-sdlc.md mention the old API.
    Note: plugins/os-me/test/headless_claude_test.rb and wrong_cost_test.rb use Decision::Model::Client, not LLM::Client; leave them (they belong to #1478 / #1488). Body line numbers are stale; use rg.

Out of scope: no change outside cc-os, no Gemfile.lock edits, no labels.

## Migration brief (2026-11-02) Self-contained start sheet for cc-os #1502. Read this, then the body. ### Body edits made today - Replaced `git: ..., ref: <sha>` pinning with the bundler `path:` flow (user decision). The Gemfile row now names the real current lines: `plugins/os-me/Gemfile:12`, `plugins/os-adr/Gemfile:9`, `plugins/os-sdlc/Gemfile:14` (the body said 10, 7, 12), and the real `path:` text. - Said the llm-stack branch is pushed. - Fixed the Claude Code error names. The body said `LLM::ClaudeCode::TimedOut` and `ProcessFailed`. The rebuild has top-level `LLM::TimedOut`, `LLM::ProcessFailed`, `LLM::ReplyFailed` (all `< RubyLLM::Error`). There is no `LLM::ClaudeCode::` error namespace. - Acceptance criterion 2: `LLM::TimedOut` is a NEW rebuild class with an old name, so a use of the new class is allowed. - Rewrote the pin criteria (1 and 7) for the `path:` flow. ### State of llm-stack Branch `rebuild/rubyllm-extension`, pushed to origin (forgejo jared/llm-stack). Local worktree: `~/dev/ruby-gems/llm-stack-rebuild`. Its tests pass. Live smokes passed on 2026-11-02 (`claude_code` subscription, Cloudflare jev judge, Ollama). ### Flow 1. Use a worktree or branch in cc-os (some `Gemfile.lock` files have uncommitted edits; do not stage, revert or regenerate them; stage by explicit path). Change `path:` in `plugins/os-me/Gemfile:12`, `plugins/os-adr/Gemfile:9` and `plugins/os-sdlc/Gemfile:14` from `dev/ruby-gems/llm-stack` to `dev/ruby-gems/llm-stack-rebuild`. 2. Migrate the call sites. Get all three suites green. 3. Do NOT merge. Report to Jared that the branch is green. llm-stack then merges the rebuild to main. 4. Change the three `path:` values back to `dev/ruby-gems/llm-stack`, run the suites again, then merge. ### API reference (rebuild worktree; confirm names there) `README.md`, `docs/design.md`, `docs/adr/0004*`, `docs/parity/llm-stack.md` ("Consumer call sites"), `docs/parity/rebuild-plan.md` section 4. - Removed: `LLM::Client`, `LLM::Http::Errors::*`, `LLM::PriceTable` (use `message.cost.total`), `LLM::Response`, `LLM::Decision`, `LLM::Usage`, `LLM::Embedding`, `LLM::ProviderError`. - Kept: `LLM::Profile.find/from_h`, `LLM::InvalidProfile`, `LLM::Error` (StandardError; gem configuration errors only). - New, all `< RubyLLM::Error`: `LLM::InvalidJudgment`, `LLM::BadReply`, `LLM::TimedOut`, `LLM::ProcessFailed`, `LLM::ReplyFailed`. Callers rescue `RubyLLM::Error` (plus `Faraday::TimeoutError`, `Faraday::ConnectionFailed`, `LLM::Error` where needed). - `ruby_llm` pinned exactly at 2.1.1. `claude_code` models: `haiku`, `sonnet`, `opus`. `cwd` is required for `claude_code`. ### Call sites that still use the removed API (rg, 2026-11-02, cc-os 268d6a9e; paths verified) os-me: - `plugins/os-me/lib/os_me/headless_runner.rb:20` (LLM::Client.new), `:26` (LLM::Response) - `plugins/os-me/lib/os_me/ollama_embedder.rb:6` - `plugins/os-me/test/llm_embed_stub.rb:5` (LLM::Embedding, LLM::Usage), `:21-25` (LLM::Client) - `plugins/os-me/test/headless_runner_cost_test.rb:16-20` (LLM::Client, PriceTable) - `plugins/os-me/test/headless_runner_chat_test.rb:29` (LLM::TimedOut: old class), `:35` (LLM::ProviderError) os-adr: - `plugins/os-adr/lib/adr/judge/model_chat.rb:15` (LLM::Client), `:20` (LLM::TimedOut), `:22` (LLM::ProviderError) os-sdlc: - `plugins/os-sdlc/eval/ticket-takedown/lib/takedown/adapter/claude_executor.rb:7` (PriceTable), `:19` (TimedOut, ProviderError), `:26` (Client) - `plugins/os-sdlc/eval/ticket-takedown/lib/takedown/parser/claude_reply.rb:13` (LLM::Response) Docs: `docs/llm-stack/spec.md`, `docs/adr/0301-llm-stack-gem-is-the-shared-llm-layer-pinned-per-plugin-from-forgejo.md`, `docs/adr/0303-llm-stack-gem-is-the-shared-llm-layer-pinned-per-plugin-from-forgejo.md` (both ADR titles are the same; read both). Also `docs/implementation-status/os-me.md` and `os-sdlc.md` mention the old API. Note: `plugins/os-me/test/headless_claude_test.rb` and `wrong_cost_test.rb` use `Decision::Model::Client`, not `LLM::Client`; leave them (they belong to #1478 / #1488). Body line numbers are stale; use `rg`. Out of scope: no change outside cc-os, no `Gemfile.lock` edits, no labels.
Author
Owner

Work started 2026-11-02 on branch migrate/llm-stack-rebuild (worktree .claude/worktrees/agent-af87e80dcb876e85c), commits 85a6dde8, 21dd9a64, f050bf5b. Not merged. os-adr, os-sdlc and takedown suites are green. os-me has 1 failure: a request with no model now sends --model gpt-5.6 (RubyLLM default) to the claude CLI; the old client sent no --model. AC7 waits for the llm-stack merge.

Work started 2026-11-02 on branch `migrate/llm-stack-rebuild` (worktree .claude/worktrees/agent-af87e80dcb876e85c), commits 85a6dde8, 21dd9a64, f050bf5b. Not merged. os-adr, os-sdlc and takedown suites are green. os-me has 1 failure: a request with no model now sends `--model gpt-5.6` (RubyLLM default) to the claude CLI; the old client sent no `--model`. AC7 waits for the llm-stack merge.
Author
Owner

Blocked on llm-stack #21 (jared/llm-stack#21): a no-model claude_code call sends --model gpt-5.6. Ruling 2026-11-02: fix in llm-stack, not in os-me. After #21 lands on the rebuild branch, re-run the os-me suite on migrate/llm-stack-rebuild.

Blocked on llm-stack #21 (https://forgejo.swansoncloud.com/jared/llm-stack/issues/21): a no-model claude_code call sends `--model gpt-5.6`. Ruling 2026-11-02: fix in llm-stack, not in os-me. After #21 lands on the rebuild branch, re-run the os-me suite on `migrate/llm-stack-rebuild`.
Author
Owner

Green 2026-11-02 against llm-stack rebuild 7c80f38 (fix for llm-stack #21). Branch migrate/llm-stack-rebuild: os-me 447 runs 0 failures; os-adr 144/0; os-sdlc 3140/0; takedown 608/0. Remaining: AC7 after the llm-stack merge to main.

Green 2026-11-02 against llm-stack rebuild 7c80f38 (fix for llm-stack #21). Branch `migrate/llm-stack-rebuild`: os-me 447 runs 0 failures; os-adr 144/0; os-sdlc 3140/0; takedown 608/0. Remaining: AC7 after the llm-stack merge to main.
Author
Owner

Resolution

Done: Migrated os-me, os-adr and the os-sdlc takedown to the RubyLLM-native llm-stack (llm-stack ADR 0004): profile.context chats and embeds, message.cost.total, message.raw, rescue RubyLLM::Error. Amended docs/llm-stack/spec.md and ADR-0303. Gemfiles point at dev/ruby-gems/llm-stack again after the llm-stack merge (29fe64b).

Evidence: main 00b91803 (commits 85a6dde8, 21dd9a64, f050bf5b, 68548df0). Against llm-stack main 29fe64b: os-me 447 runs, os-adr 144, os-sdlc 3140, takedown 608, 0 failures, 0 errors. No-model regression fixed upstream in llm-stack #21 (7c80f38). Lock diffs are only the llm-stack ruby_llm pin line.

Follow-ups: none. ADR-0301 left unchanged on purpose: it is superseded by 0303 and the amend gate rejects pointers on superseded history. ADR-0303 decision 4 wording about path: as a local override is a doc nit, dropped.

## Resolution **Done:** Migrated os-me, os-adr and the os-sdlc takedown to the RubyLLM-native llm-stack (llm-stack ADR 0004): profile.context chats and embeds, message.cost.total, message.raw, rescue RubyLLM::Error. Amended docs/llm-stack/spec.md and ADR-0303. Gemfiles point at dev/ruby-gems/llm-stack again after the llm-stack merge (29fe64b). **Evidence:** main 00b91803 (commits 85a6dde8, 21dd9a64, f050bf5b, 68548df0). Against llm-stack main 29fe64b: os-me 447 runs, os-adr 144, os-sdlc 3140, takedown 608, 0 failures, 0 errors. No-model regression fixed upstream in llm-stack #21 (7c80f38). Lock diffs are only the llm-stack ruby_llm pin line. **Follow-ups:** none. ADR-0301 left unchanged on purpose: it is superseded by 0303 and the amend gate rejects pointers on superseded history. ADR-0303 decision 4 wording about path: as a local override is a doc nit, dropped.
jared closed this issue 2026-11-02 21:49:46 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1502
No description provided.