Skip to content

feat(prompt): tell the agent which model runs it, its subagents, and the advisor - #860

Merged
matthewyjiang merged 7 commits into
mainfrom
worktree/lucky-valley-387c
Aug 11, 2026
Merged

feat(prompt): tell the agent which model runs it, its subagents, and the advisor#860
matthewyjiang merged 7 commits into
mainfrom
worktree/lucky-valley-387c

Conversation

@matthewyjiang

@matthewyjiang matthewyjiang commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Summary

The agent had no way to know what model it was running on. That is a fact only Rho holds: the user picks the model, Rho can switch it mid-session, and the model itself can be newer than its own training data. The same blind spot covered its subagents and the advisor.

Now:

  • The system prompt names the running model, and the advisor's model when advisor mode is on.
  • Each delegated run reports the model it used in its own output.
  • Switching the conversation model or the advisor model appends one short line.

Model ids lead and catalog names follow, read from the models.dev name field:

You are running on openai-codex/gpt-5.6-luna (GPT-5.6 Luna).
The `advisor` tool consults anthropic/claude-fable-5 (Claude Fable 5).
[conversation model switched to anthropic/claude-fable-5 (Claude Fable 5)]

The id always leads because it is the part a reader can act on: it picks the provider route, /model takes it back, and provider docs use it. A name is never invented from an id, so an unknown model shows its id alone rather than a guess. models.toml gains a display_name override for models no catalog knows, such as local Ollama builds.

Claude Code aliases. Rho passes --model through untouched and the binary lists no models, so opus is a pointer Rho cannot follow. The only place the model behind it appears is the system/init frame at the start of a run, so Rho records what those frames report:

model: claude-code/claude-opus-4-6 (Claude Opus 4.6)

That store is deliberately process-local. An alias points at whichever model is current, and a mapping saved to disk would outlive that and name a retired model with confidence.

Written text is never rewritten

The agent and advisor tool descriptions name no model at all. Both would otherwise go stale or silently change what the caller was already told: an inheriting agent's model changes when the conversation model switches, a pinned model gains its name once the catalog prefetch lands, and /advisor swaps the reviewer without rebuilding the tool list. Every model fact instead lives where it is settled - the system prompt, a run's own output, or an appended notice.

Catalog names resolve once per process, because each lookup otherwise opens a fresh sqlite connection and runs its schema statements on paths that format one per delegated run. A catalog write drops that provider's resolved names, so a name that lands during a session reaches the next text that names the model. Text already produced is untouched: an entry only changes when the catalog underneath it does.

Why names were missing for openai-codex

openai-codex sells OpenAI models through Codex OAuth and has no models.dev entry of its own; it already read openai upstream, so parsing was never the problem. The gap was that the name cache is only ever filled by selecting a model, so a model the session merely names - a subagent target, an advisor - was never fetched. Startup now prefetches names for every model the session can name, in the background, with one models.dev download for the whole set rather than one per model. A warm cache does no network at all.

Validation

cargo test --workspace                # 2839 passed, 0 failed (run 5x, stable)
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo fmt --all
python3 scripts/check_architecture.py                        # passed
python3 scripts/check_sdk_compatibility.py --test-downstream # passed
bash scripts/check_docs_ui_demo.sh --check                   # OK (3 SVGs unchanged)
./scripts/check_crate_publish_prep.sh                        # passed

Three defects found and fixed while validating:

  • run_model_line called the name lookup for every formatted snapshot, and each lookup opened a fresh sqlite connection and ran its schema statements. Under parallel tests that stalled the suite for minutes; it would also have hit live agents list / status. Fixed by resolving names once per process.
  • Resolving once per process then kept the misses too, so the startup prefetch could not help the launch it ran on: the system prompt asks for every name it wants before the download can finish, and those models would show no name until the next launch. Fixed by dropping a provider's resolved names when a catalog write lands.
  • Two of my new tests read process-global stores and raced with each other. Fixed by asserting the decision rather than the rendered string, sharing one lock over the store, and giving the name-cache tests ids no other test uses.

Review follow-ups on top of that:

  • A description is built from config ids and a downloaded catalog name, then written into one prompt line or one bracketed notice. A newline in any part added a line the executor reads as its own instruction. describe now replaces control characters with spaces.
  • Changing only the advisor reasoning level appended "advisor model switched to" the model the advisor already used, and a failed notice append in that branch left the store holding a reviewer the executor was never told about. The comparison now uses the identity the notice reports, and the branch restores the previous model like the enable and disable path.

Docs TUI proof plate

  • Ran bash scripts/check_docs_ui_demo.sh --check (or --write and committed dark SVGs + site light SVG)

Test gate

  • Followed rho-test-selection (failure mode, owner layer, gap).
  • Each new test names a distinct failure mode (user-visible or contract bug).
  • Each new test has one owner layer (pure unit / SDK contract / PTY / OS).
  • No existing test already covers that failure mode at a better layer.
  • Interactive TUI behavior uses a PTY scenario by default; new crates/rho/src/tui unit tests are pure logic or justified below.
  • Cases share one test function per rule (tables), not twin functions per literal.
  • Asserts use structured values; string .contains only for redaction, wire format, or security escaping.
  • No locks on help text, statusline chrome, labels, or other copy.
  • No wall-clock sleep used for synchronization; no known-flaky timing races.
  • Nearby weaker or duplicate tests were removed or merged when practical.

While applying the gate I merged three near-duplicate resolved_models tests into one table, dropped a test that only restated a match arm, and replaced prompt-prose contains asserts with assembly-seam asserts on the model reference. IsolatedRhoHome now also isolates HOME, because agent discovery reads ~/.rho/agents and a developer's own agent files changed what the catalog held.

New tests

Failure mode Owner layer Why existing coverage is not enough
A wrong name-source order shows a stale name, or echoes a model id back as its own name Pure unit New resolver
models.dev name parsed wrong, or a blank name stored as a name Pure unit (parser) New field
A provider reading another provider's upstream catalog silently loses names (openai-codex) Pure unit The reported bug; no test covered the upstream indirection
Startup prefetch hits the network when every target is already cached Pure unit New code path on every launch
A --model alias and the unpinned default collapse into one entry, or a blank report is stored Pure unit New store
A model id renders without its name, or a Claude alias renders without what it resolved to Pure unit New rendering
Only the init frame states the model a run bound; another frame's model would mislabel the run Pure unit (wire contract) New protocol field
The predicted agent model disagrees with the model binding picks, so startup prefetches one model's name and the run uses another Pure unit Two code paths that must agree; neither side alone catches drift
The bound model never reaches the assembled system prompt Runtime contract Covers the wiring, not the wording
The advisor description names the reviewer and so rewrites what the executor was told Pure unit Invariant introduced by this PR
The agent list names a model and so goes stale on a switch Pure unit Invariant introduced by this PR
A run reports the wrong model, or reports the unpinned-Claude placeholder as a model name Pure unit New output field
An advisor model change while advisor mode stays on tells the executor nothing Runtime contract No tool list changes, so nothing else reports it
A model switch tells the executor nothing, or a first model choice is reported as a switch Runtime contract Notice is appended from the TUI switch path
A name that lands after a lookup missed stays hidden for the process, stranding the startup prefetch Pure unit Invariant introduced by this PR
A model id or a downloaded catalog name carrying a newline adds a line to the system prompt or splits a notice Pure unit Invariant introduced by this PR

PTY exception (if any)

N/A - no interactive TUI layout, chrome, or input behavior changed. The two TUI-path
tests drive the model-switch and advisor-switch entry points and assert appended
model-visible history, not rendering.

Breaking changes

None. ModelMetadata::display_name and RunStatus::claude_model are additive with serde defaults. The models.dev cache version moves 7 to 8 so existing rows refetch and pick up names.

rho-providers is cut to 0.21.0 in the same PR, because app packaging verifies internal dependencies against crates.io and the new display_name module has to ship as an unpublished same-cut dependency.


Written by Claude Opus 4.6 in Claude Code.

Summary by CodeRabbit

  • New Features

    • Model references now show friendly display names when available, with clear provider/model fallbacks.
    • Prompts and completed agent summaries identify the active model, including Claude’s resolved model.
    • Model metadata is prefetched for faster, more consistent display.
    • Conversation and advisor model changes now generate contextual notices.
    • Runtime model switching now provides improved transition handling and recovery.
  • Bug Fixes

    • Corrected handling of unnamed or unpinned Claude models.
    • Improved model identity reporting across aliases, configured models, and completed runs.
  • Tests

    • Added coverage for display names, model switching, Claude reporting, and fallback behavior.

…the advisor

The agent had no way to know what model it was running on. That is a fact only
Rho holds: the user picks the model, and Rho can switch it mid-session.

The system prompt now names the running model and the advisor's model. Each
delegated run reports the model it used in its own output. Switching either
model appends one short line.

Model ids lead and catalog names follow, from the models.dev `name` field:

    openai-codex/gpt-5.6-luna (GPT-5.6 Luna)

Names are never invented from an id, because a model can be newer than whatever
reads the prompt. Rho passes Claude Code `--model` through untouched and the
binary lists no models, so an alias like `opus` is resolved from the model the
run reports in its stream-json init frame.

Text already written is never rewritten: the `agent` and `advisor` tool
descriptions name no model, and catalog names resolve once per process.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f525b5fc-33a6-4ac9-82b0-044a248ae56f

📥 Commits

Reviewing files that changed from the base of the PR and between b793181 and b804ab4.

📒 Files selected for processing (11)
  • crates/rho/src/app/agent_binding.rs
  • crates/rho/src/app/agent_binding_tests.rs
  • crates/rho/src/app/interactive_runtime.rs
  • crates/rho/src/app/interactive_runtime_advisor.rs
  • crates/rho/src/app/interactive_runtime_provider.rs
  • crates/rho/src/model_identity.rs
  • crates/rho/src/model_identity_tests.rs
  • crates/rho/src/prompt.rs
  • crates/rho/src/tools/agent/agent_tests.rs
  • crates/rho/src/tools/agent_output.rs
  • crates/rho/tests/tui_pty.rs
💤 Files with no reviewable changes (1)
  • crates/rho/src/tools/agent/agent_tests.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/rho/src/app/interactive_runtime_advisor.rs
  • crates/rho/src/tools/agent_output.rs
  • crates/rho/src/model_identity_tests.rs
  • crates/rho/src/app/agent_binding_tests.rs
  • crates/rho/src/prompt.rs
  • crates/rho/src/app/agent_binding.rs

📝 Walkthrough

Walkthrough

The PR adds cached model display-name resolution, unified Rho and Claude model identities, model-aware prompts, model-switch notices, resolved Claude model tracking, startup metadata prefetching, and related tests and version updates.

Changes

Model identity integration

Layer / File(s) Summary
Catalog display names and prefetch
crates/rho-providers/src/model/*, .release-please-manifest.json, crates/rho-providers/Cargo.toml, crates/rho/Cargo.toml
Model metadata now includes validated display names. Lookups use synchronized, generation-aware caching and provider fallback. Startup metadata prefetching deduplicates targets.
Claude resolved-model tracking
crates/rho/src/claude_runtime/stream/*, crates/rho/src/subagent.rs, crates/rho/src/app/agent_executor.rs
Claude init frames provide the concrete model ID, which is carried through status patches into run status.
Unified identity and run reporting
crates/rho/src/model_identity.rs, crates/rho/src/model_identity_tests.rs, crates/rho/src/app/agent_binding.rs, crates/rho/src/app/agent_binding_tests.rs, crates/rho/src/tools/agent_output.rs
PromptModel represents Rho and Claude identities, formats model descriptions, predicts bound models, and preserves requested versus resolved Claude values.
Prompt and runtime model context
crates/rho/src/prompt.rs, crates/rho/src/app/*, crates/rho/src/tools/*, crates/rho/src/tui/*
Prompts and run output include model identity. Conversation and advisor model changes create persisted notices with rollback handling.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentBinding
  participant PromptAssembly
  participant InteractiveRuntime
  participant AgentOutput
  AgentBinding->>PromptAssembly: provide PromptModel
  PromptAssembly->>InteractiveRuntime: render model-aware prompt
  InteractiveRuntime->>InteractiveRuntime: record model-switch notice
  InteractiveRuntime->>AgentOutput: persist run status
  AgentOutput->>AgentOutput: format recorded model identity
Loading

Possibly related PRs

Suggested reviewers: pullfrog

Poem

I’m a rabbit with models to name,
Catalog names hop into the frame.
Claude’s resolved ID joins the view,
Switch notices say what changed too.
Prompts and runs now report each part.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: exposing the active model to the agent, subagents, and advisor.
Description check ✅ Passed The description covers the required summary, validation, TUI proof, test gate, new tests, PTY exception, and breaking-change status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The detached catalog prefetch races with a process-lifetime negative cache, so the new display names can be missing for the entire first launch.

Reviewed changes across the model identity, catalog metadata, prompt assembly, Claude stream handling, delegated-run output, and interactive model-switch paths.

  • Model identity and names — Adds catalog-backed names, Claude Code alias resolution, and a shared representation for prompt-facing model identity.
  • Prompt and switch context — Names the running and advisor models in system context and appends notices when either changes.
  • Delegated-run reporting — Captures Claude's concrete model from init frames and includes model identity in run snapshots and completions.
  • Startup metadata prefetch — Collects models the session may name and starts a deduplicated models.dev refresh.
  • Coverage — Adds focused tests for metadata parsing, binding parity, identity rendering, stream mapping, prompt wiring, and switch notices.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT Sol𝕏

Comment thread crates/rho/src/app/bootstrap.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
crates/rho/src/tools/advisor/advisor_tests.rs (1)

353-354: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The last two assertions check a compile-time constant.

baseline is captured on line 344, before either set_model call, and TOOL_DESCRIPTION is a const. These two assertions cannot fail because of the store changes this test makes. The assert_eq! checks on lines 347 and 352 already prove the invariance the test name claims.

Either drop the two lines or assert against the post-change description so they exercise the same path.

As per coding guidelines: "avoid static-constant tests, removed-behavior tests, and string-contains tests that merely lock copied text."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/tools/advisor/advisor_tests.rs` around lines 353 - 354, Remove
the redundant baseline.contains assertions in the test around the set_model
calls; the existing assert_eq checks already verify that the description remains
unchanged, so do not add further static string-contents assertions.

Source: Coding guidelines

crates/rho/src/app/interactive_runtime_tests.rs (1)

877-889: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist last_notice_text to module scope.

The same block-extraction logic already appears twice in this file, at lines 675-681 and 701-707. This nested copy makes three. Move last_notice_text to module scope and call it from advisor_mode_changes_the_tool_list_without_replacing_the_session as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/app/interactive_runtime_tests.rs` around lines 877 - 889, Move
the nested last_notice_text helper to module scope so it is defined once, then
update advisor_mode_changes_the_tool_list_without_replacing_the_session and the
other duplicated call sites to reuse it. Remove the local duplicate while
preserving the existing user-message text extraction behavior.
crates/rho/src/claude_runtime/resolved_models.rs (2)

60-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Match StreamEffect exhaustively so a new variant forces a decision.

note_stream_effect uses a let-else that discards every non-Status variant silently. If a future variant also carries a resolved model, this function keeps compiling and keeps dropping it.

♻️ Proposed exhaustive match
-    let super::stream::StreamEffect::Status(patch) = effect else {
-        return;
-    };
-    if let Some(model) = &patch.claude_model {
-        record(requested, model);
-    }
+    match effect {
+        super::stream::StreamEffect::Status(patch) => {
+            if let Some(model) = &patch.claude_model {
+                record(requested, model);
+            }
+        }
+        // No other effect carries a resolved model today.
+        _other => {}
+    }

Replace _other => {} with the concrete variant list so the compiler flags additions.

As per coding guidelines: "Match known Rust enums exhaustively so new variants require intentional handling."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/claude_runtime/resolved_models.rs` around lines 60 - 67,
Update note_stream_effect to use an exhaustive match on StreamEffect instead of
the let-else pattern that silently ignores non-Status variants. Handle the
existing concrete variants explicitly, preserving model recording for Status, so
adding any future StreamEffect variant produces a compiler error requiring an
intentional decision.

Source: Coding guidelines


40-45: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider recovering from lock poisoning in record and last_resolved.

Both accessors call .expect("resolved Claude model lock"). A panic anywhere inside the critical section poisons the RwLock for the rest of the process. Every later describe() on a Claude identity then panics, which turns a name-lookup detail into a session-wide failure. The stored data stays valid, because the guarded operations are plain String inserts and clones.

test_lock already uses unwrap_or_else(|poisoned| poisoned.into_inner()). The same treatment here keeps model naming best-effort.

🛡️ Proposed poisoning recovery
-    let mut store = store().write().expect("resolved Claude model lock");
+    let mut store = store()
+        .write()
+        .unwrap_or_else(|poisoned| poisoned.into_inner());
-    let store = store().read().expect("resolved Claude model lock");
+    let store = store()
+        .read()
+        .unwrap_or_else(|poisoned| poisoned.into_inner());

Also applies to: 70-71

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/claude_runtime/resolved_models.rs` around lines 40 - 45,
Update record and last_resolved to recover from poisoned RwLock guards using the
same unwrap_or_else(|poisoned| poisoned.into_inner()) pattern already used by
test_lock, replacing the expect-based lock acquisition while preserving the
existing guarded insert and clone behavior.
crates/rho/src/model_identity.rs (1)

113-132: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a named type for the prefetch keys.

describable_models returns Vec<(String, String)>. The order of provider and model is only documented in prose, so a call site can swap them without a compile error. A small newtype or a struct with provider and model fields makes the contract explicit and matches the guideline on self-documenting call sites.

As per coding guidelines: "Make Rust call sites self-documenting by preferring enums, named methods, builders, or newtypes over ambiguous boolean or Option parameters."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/model_identity.rs` around lines 113 - 132, Replace the
ambiguous tuple return type of describable_models with a named type containing
explicit provider and model fields, and construct that type in the
ModelIdentity::Rho branch. Update affected callers to access the named fields so
provider/model ordering is enforced and call sites are self-documenting.

Source: Coding guidelines

crates/rho/src/tools/agent/agent_tests.rs (1)

19-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

This change widens process-environment mutation in tests.

IsolatedRhoHome::new now sets HOME in addition to RHO_HOME. Both are process-global. crate::paths::process_env_lock() serializes only the tests that take that lock; any concurrent test that reads HOME without it observes the temp directory.

The repository guideline asks for injection instead of process-environment mutation. Agent discovery would need a home-directory parameter or an injected resolver to satisfy it, so this is a larger change than the current diff. Track it rather than fix it inline if that plumbing is out of scope here.

As per coding guidelines: "Do not mutate the process environment in tests; inject environment-derived values instead."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/tools/agent/agent_tests.rs` around lines 19 - 41, Track this
as a follow-up rather than expanding the current test change: avoid setting HOME
in IsolatedRhoHome::new, and update agent discovery to accept an injected
home-directory/resolver so tests can isolate ~/.rho/agents and ~/.agents/agents
without process-environment mutation.

Source: Coding guidelines

crates/rho/src/tui/model_actions_tests.rs (1)

309-370: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Hoist the duplicated fixtures to module scope.

switch_to_anthropic is now defined twice in this file, here and at lines 194-210. The credential and App setup in app_on_openai also repeats the block at lines 219-239. The pattern has repeated, so the shared mechanics can move out.

Move app_on_openai and switch_to_anthropic to module scope and call them from select_model_report_auto_edit_tool_follows_provider_change. Keep the differing policy at the call sites: the older test needs the EditTool::Auto and pinned config variations and asserts the handoff report.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/rho/src/tui/model_actions_tests.rs` around lines 309 - 370, Hoist the
shared `app_on_openai` and `switch_to_anthropic` helpers to module scope,
removing their duplicate local definitions. Update
`select_model_report_auto_edit_tool_follows_provider_change` and the older test
to call these helpers, while retaining each test’s distinct `EditTool::Auto`,
pinned configuration, and handoff-report assertions at its call site.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/rho-providers/src/model/display_name.rs`:
- Around line 43-53: Update model_display_name so the resolved name is
reconciled under the cache write lock: after acquiring the lock, check whether
the key was inserted by another caller and return that existing value; otherwise
insert and return the newly resolved name. Preserve the initial read fast path
and use the cache entry selected while holding the write lock.

In `@crates/rho/src/app/interactive_runtime_advisor.rs`:
- Around line 71-78: Update the changed-model comparison in the interactive
runtime advisor flow to compare the reported model identities, excluding
reasoning-only differences. Use the same provider/model identity representation
consumed by advisor_model_switch_context and ModelIdentity::describe, while
preserving notices for actual provider or model changes.
- Around line 79-84: Update the notice-handling branch in the interactive
runtime advisor around store.set_model and append_user_context_with_display to
capture the previous advisor model before switching, then restore it if
appending the notice fails. Match the adjacent enable/disable rollback behavior
and preserve the existing success return path.

In `@crates/rho/src/prompt.rs`:
- Around line 110-124: The ModelIdentity::describe output can contain control
characters, but prompt construction assumes it is single-line. Sanitize or
encode the display-name resolution used by ModelIdentity::describe so provider,
model, and catalog names cannot introduce newlines or other control characters;
this root fix covers the running-model and advisor lines at
crates/rho/src/prompt.rs:110-124 and ensures model_switch_context and
advisor_model_switch_context at crates/rho/src/prompt.rs:284-302 always emit
exactly one bracketed line.

---

Nitpick comments:
In `@crates/rho/src/app/interactive_runtime_tests.rs`:
- Around line 877-889: Move the nested last_notice_text helper to module scope
so it is defined once, then update
advisor_mode_changes_the_tool_list_without_replacing_the_session and the other
duplicated call sites to reuse it. Remove the local duplicate while preserving
the existing user-message text extraction behavior.

In `@crates/rho/src/claude_runtime/resolved_models.rs`:
- Around line 60-67: Update note_stream_effect to use an exhaustive match on
StreamEffect instead of the let-else pattern that silently ignores non-Status
variants. Handle the existing concrete variants explicitly, preserving model
recording for Status, so adding any future StreamEffect variant produces a
compiler error requiring an intentional decision.
- Around line 40-45: Update record and last_resolved to recover from poisoned
RwLock guards using the same unwrap_or_else(|poisoned| poisoned.into_inner())
pattern already used by test_lock, replacing the expect-based lock acquisition
while preserving the existing guarded insert and clone behavior.

In `@crates/rho/src/model_identity.rs`:
- Around line 113-132: Replace the ambiguous tuple return type of
describable_models with a named type containing explicit provider and model
fields, and construct that type in the ModelIdentity::Rho branch. Update
affected callers to access the named fields so provider/model ordering is
enforced and call sites are self-documenting.

In `@crates/rho/src/tools/advisor/advisor_tests.rs`:
- Around line 353-354: Remove the redundant baseline.contains assertions in the
test around the set_model calls; the existing assert_eq checks already verify
that the description remains unchanged, so do not add further static
string-contents assertions.

In `@crates/rho/src/tools/agent/agent_tests.rs`:
- Around line 19-41: Track this as a follow-up rather than expanding the current
test change: avoid setting HOME in IsolatedRhoHome::new, and update agent
discovery to accept an injected home-directory/resolver so tests can isolate
~/.rho/agents and ~/.agents/agents without process-environment mutation.

In `@crates/rho/src/tui/model_actions_tests.rs`:
- Around line 309-370: Hoist the shared `app_on_openai` and
`switch_to_anthropic` helpers to module scope, removing their duplicate local
definitions. Update `select_model_report_auto_edit_tool_follows_provider_change`
and the older test to call these helpers, while retaining each test’s distinct
`EditTool::Auto`, pinned configuration, and handoff-report assertions at its
call site.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 28b155e1-0f46-4485-b2a1-74fb42f49910

📥 Commits

Reviewing files that changed from the base of the PR and between ee2de62 and 1c04ce4.

📒 Files selected for processing (34)
  • crates/rho-providers/src/model/display_name.rs
  • crates/rho-providers/src/model/display_name_tests.rs
  • crates/rho-providers/src/model/mod.rs
  • crates/rho-providers/src/model/models_dev.rs
  • crates/rho-providers/src/model/models_dev_tests.rs
  • crates/rho/src/app/agent_binding.rs
  • crates/rho/src/app/agent_binding_tests.rs
  • crates/rho/src/app/bootstrap.rs
  • crates/rho/src/app/interactive_runtime_advisor.rs
  • crates/rho/src/app/interactive_runtime_tests.rs
  • crates/rho/src/app/mod.rs
  • crates/rho/src/app/tools_prompt.rs
  • crates/rho/src/app/tools_prompt_tests.rs
  • crates/rho/src/claude_runtime/mod.rs
  • crates/rho/src/claude_runtime/one_shot.rs
  • crates/rho/src/claude_runtime/resolved_models.rs
  • crates/rho/src/claude_runtime/resolved_models_tests.rs
  • crates/rho/src/claude_runtime/session.rs
  • crates/rho/src/claude_runtime/stream/presentation.rs
  • crates/rho/src/claude_runtime/stream/protocol.rs
  • crates/rho/src/claude_runtime/stream/stream_protocol_tests.rs
  • crates/rho/src/claude_runtime/stream/types.rs
  • crates/rho/src/lib.rs
  • crates/rho/src/model_identity.rs
  • crates/rho/src/model_identity_tests.rs
  • crates/rho/src/prompt.rs
  • crates/rho/src/subagent.rs
  • crates/rho/src/tools/advisor/advisor_tests.rs
  • crates/rho/src/tools/advisor/mod.rs
  • crates/rho/src/tools/agent/agent_tests.rs
  • crates/rho/src/tools/agent/mod.rs
  • crates/rho/src/tools/agent_output.rs
  • crates/rho/src/tui/model_actions.rs
  • crates/rho/src/tui/model_actions_tests.rs

Comment thread crates/rho-providers/src/model/display_name.rs
Comment thread crates/rho/src/app/interactive_runtime_advisor.rs Outdated
Comment thread crates/rho/src/app/interactive_runtime_advisor.rs
Comment thread crates/rho/src/prompt.rs
Names resolved once per process and kept the answer, including a miss. The
startup prefetch cannot beat the system prompt to the first lookup, so every
model the prompt named pinned `None` and the names it fetched first appeared
on the next launch.

Catalog writes now drop that provider's resolved names, so the next text to
name the model carries the name. Text already produced is untouched: an entry
only changes when the catalog underneath it does.
App packaging verifies internal dependencies against crates.io, so the new
`display_name` module and metadata prefetch must ship as an unpublished
same-cut dependency.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The cache invalidation fix still permits an in-flight lookup to restore a stale result after the writer invalidates it.

Reviewed changes since the prior Pullfrog review at 1c04ce4, focusing on the catalog-cache fix and provider crate release.

  • Invalidated display-name entries — Added provider-wide invalidation after models.dev and provider-model catalog writes, plus sequential regression coverage for cached misses.
  • Released the provider API — Bumped rho-providers to 0.21.0 and updated Rho's dependency, lockfile, and release manifest.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT Sol𝕏

Comment thread crates/rho-providers/src/model/models_dev.rs
Review follow-ups.

- A description is built from config ids and a downloaded catalog name, then
  written into one prompt line or one bracketed notice. A newline in any part
  added a line the executor reads as its own instruction. `describe` now
  replaces control characters with spaces.
- A lookup that resolved before a catalog write landed could still cache its
  older answer over the new row, which is the miss the prefetch exists to
  clear. Writes now bump a generation the lookup checks before it caches.
- Changing only the advisor reasoning level appended "advisor model switched
  to" the model the advisor already used. The comparison now uses the identity
  the notice reports.
- A failed notice append in that branch left the store holding a reviewer the
  executor was never told about. It now restores the previous model, matching
  the enable and disable path.
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 11, 2026
pullfrog[bot]
pullfrog Bot previously approved these changes Aug 11, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes since the prior Pullfrog review at 119b8ce, covering the cache-race correction and prompt/advisor hardening.

  • Guarded cache insertion — Added a catalog generation check so an in-flight pre-write lookup cannot restore stale model-name data after invalidation.
  • Kept descriptions single-line — Replaced control characters in externally sourced model identity text before it reaches prompts or switch notices.
  • Corrected advisor notices — Compared prompt-visible model identity to suppress reasoning-only switch notices and restored the previous advisor model when notice insertion fails.
  • Expanded focused coverage — Added regression cases for single-line model descriptions and reasoning-only advisor changes.

Pullfrog  | View workflow run | Using GPT Sol𝕏

…tices

Delete the process-global Claude resolution store so PromptModel::describe is
pure. Rename away from SDK ModelIdentity, share bind's model-policy path for
prefetch prediction, drop the claude-cli model sentinel, move conversation
switch notices into InteractiveRuntime with rollback, and route attach through
the same formatter.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The new conversation-switch rollback can leave compaction bound to the rejected model.

Reviewed changes since the prior Pullfrog review at 001880f, covering the model-label ownership refactor and centralized switch handling.

  • Made model labels value-based — Replaced ambient Claude alias state with PromptModel values that preserve requested and resolved identities on each run.
  • Centralized switch notices — Moved conversation and advisor notice generation into shared model-label logic and made conversation switch failures restore the previous provider.
  • Unified status rendering — Routed delegated output and TUI attachment identity lines through the same model representation, including honest unpinned-Claude labels.
  • Aligned binding prediction — Shared Rho model policy application between launch binding and startup catalog prefetch prediction.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT Sol𝕏

Comment thread crates/rho/src/app/interactive_runtime.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/rho/src/app/interactive_runtime.rs`:
- Around line 695-715: Update the append_user_context_with_display error path in
the interactive runtime to capture and handle refresh_compaction failure after
provider restoration instead of discarding it. Return an InvalidConfiguration
error that reports both the original switch-recording failure and compaction
rollback failure, while preserving the existing provider-rollback error path;
add a regression test covering this failed compaction rollback.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b8db6b77-79ac-4dab-b194-73861e722aa0

📥 Commits

Reviewing files that changed from the base of the PR and between 1c04ce4 and b793181.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • .release-please-manifest.json
  • crates/rho-providers/Cargo.toml
  • crates/rho-providers/src/model/display_name.rs
  • crates/rho-providers/src/model/display_name_tests.rs
  • crates/rho-providers/src/model/models_dev.rs
  • crates/rho-providers/src/model/provider_models.rs
  • crates/rho/Cargo.toml
  • crates/rho/src/app/agent_binding.rs
  • crates/rho/src/app/agent_binding_tests.rs
  • crates/rho/src/app/agent_executor.rs
  • crates/rho/src/app/bootstrap.rs
  • crates/rho/src/app/interactive_runtime.rs
  • crates/rho/src/app/interactive_runtime_advisor.rs
  • crates/rho/src/app/interactive_runtime_tests.rs
  • crates/rho/src/app/tools_prompt.rs
  • crates/rho/src/model_identity.rs
  • crates/rho/src/model_identity_tests.rs
  • crates/rho/src/prompt.rs
  • crates/rho/src/tools/agent/agent_tests.rs
  • crates/rho/src/tools/agent_output.rs
  • crates/rho/src/tui/attachment/app.rs
  • crates/rho/src/tui/attachment/app_tests.rs
🚧 Files skipped from review as they are similar to previous changes (8)
  • crates/rho/src/app/tools_prompt.rs
  • crates/rho-providers/src/model/display_name.rs
  • crates/rho/src/app/bootstrap.rs
  • crates/rho/src/app/interactive_runtime_advisor.rs
  • crates/rho/src/tools/agent/agent_tests.rs
  • crates/rho-providers/src/model/models_dev.rs
  • crates/rho/src/app/interactive_runtime_tests.rs
  • crates/rho/src/prompt.rs

Comment thread crates/rho/src/app/interactive_runtime.rs Outdated
…iction

Finish the maintainability cleanup on model prompt labels:

- Predict agent models only when bind would succeed; skip broken policies
  instead of inventing prefetch keys from unresolved selection text.
- Own switch-notice wording entirely in prompt (ModelSwitchKind + compose);
  keep PromptModel as a pure label type.
- Delete the run_prompt_model passthrough; construct labels from run status
  or SDK identity directly.
- Share provider-switch restore in InteractiveRuntime so compaction and
  notice failures use one rollback path.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The latest refactor introduces no new issues, but it does not resolve the open compaction rollback concern: the restored provider's refresh_compaction() result is still discarded.

Reviewed changes since the prior Pullfrog review at b793181, covering the model-label cleanup and shared provider restoration.

  • Skipped unbindable prefetch targets — Changed model prediction to return no catalog key when the corresponding agent policy cannot bind.
  • Moved switch-notice policy — Relocated switch kinds and notice composition into prompt assembly while keeping PromptModel focused on model labels.
  • Removed residual wrappers — Constructed prompt models directly from SDK identity and run status at the remaining call sites.
  • Shared provider restoration — Extracted provider rollback for compaction and notice failures, while leaving the existing post-restore compaction failure path unresolved.
  • Expanded binding coverage — Added a focused test proving an invalid alias neither binds nor produces a speculative prefetch key.

Pullfrog  | Fix it ➔View workflow run | Using GPT Sol𝕏

- Move replace_provider into interactive_runtime_provider so the main
  runtime module stays under 1k and the switch path has one home.
- Surface incomplete rollback when compaction cannot follow a restored
  provider after a failed model-switch notice (pullfrog/CodeRabbit).
- Drop the agent test that only reasserted PromptModel::from_run_status.
- Accept truncated Claude session ids on attach headers once resolved
  model text lengthens the identity line (PTY CI).

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes since the prior Pullfrog review at ee0866a, covering the provider-switch extraction and rollback correction.

  • Restored compaction on rollback — Rebuilt compaction after restoring the previous provider when model-switch notice persistence fails, and surfaced an explicit combined error if that rebuild also fails.
  • Extracted provider switching — Moved the complete provider transition into interactive_runtime_provider.rs while preserving transition completion, subagent selection, MCP sampling, and live-context invalidation ordering.
  • Adjusted focused coverage — Kept delegated model reconstruction coverage at its owner layer and updated the Claude PTY assertion for the longer resolved-model identity line.

Pullfrog  | View workflow run | Using GPT Sol𝕏

@matthewyjiang
matthewyjiang merged commit d18c377 into main Aug 11, 2026
14 checks passed
@matthewyjiang
matthewyjiang deleted the worktree/lucky-valley-387c branch August 11, 2026 21:49
@github-actions github-actions Bot mentioned this pull request Aug 11, 2026
This was referenced Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant