Skip to content

feat(claude-models): resolve the Claude model list at runtime - #237

Merged
leeroybrun merged 11 commits into
happier-dev:devfrom
danljungstrom:feat/claude-dynamic-model-list
Aug 12, 2026
Merged

feat(claude-models): resolve the Claude model list at runtime#237
leeroybrun merged 11 commits into
happier-dev:devfrom
danljungstrom:feat/claude-dynamic-model-list

Conversation

@danljungstrom

@danljungstrom danljungstrom commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Claude's model list was pinned to a curated static catalog and built twice — once by the new-session
preflight probe, once by the in-session sessionModelsV1 publisher. This gives it a single owner
sourced from the Anthropic Models API, and switches the app onto it.

Why

Reported in Discord: "the models listed for cluade are very old, you have to use custom model"
(https://discord.com/channels/1467127365317558402/1478724067195355240/1535308686354944040). A static
catalog means every Claude release needs a code change plus an app release before users can select
the new model, so this complaint recurs on each launch — and the custom-model field is the workaround
people fall back to.

The CLI already published sessionModelsV1 for Claude sessions, but the app discarded it because the
catalog declared dynamicProbe: 'static-only' — an active producer with its consumer gated off.

Reading order

Two commits, meant to be read in order:

  1. 389608360 refactor: give the Claude model list a single owner (23 files) — where the list
    comes from, credential routing, effort/ultracode semantics, spawn plumbing. No user-visible
    change: Claude is still static-only at the end of this commit.
  2. af97ce02b feat!: consume the dynamic Claude model list (11 files) — the flip, plus the
    per-model metadata the dynamic UI path was dropping.

62% of the diff is tests. New production logic is concentrated in three new files —
resolveClaudeModelCatalog.ts, anthropicModelsFetch.ts, deriveDiscoveredClaudeModel.ts. The two
files worth the most attention are apps/cli/src/backends/claude/runClaude.ts (spawn path, two
independent runtime scopes) and apps/ui/sources/sync/domains/models/modelOptions.ts (the
static-vs-dynamic row builders).

How to test

  1. cd apps/ui && yarn typecheck — 0 errors.
  2. cd apps/ui && yarn vitest run sources/sync/domains/models sources/components/sessions/pickers sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.refresh.test.tsx
    — 93 passed / 9 files.
  3. cd apps/cli && yarn vitest run src/backends/claude src/capabilities — 16 failed / 308 passed
    (325 files). All 16 failures are pre-existing on dev; see Notes.
  4. Manual: new-session screen with a Claude backend shows models the account can run beyond the
    curated list; curated entries keep their labels, effort defaults, and 1M-context toggle. With no
    credential or no network, the list falls back to the curated catalog.

Notes

Credential routing. The catalog honors ANTHROPIC_BASE_URL — the built-in Z.AI, DeepSeek and
MiniMax Claude profiles point it at a gateway and pair it with a gateway-issued
ANTHROPIC_AUTH_TOKEN, so a hardcoded Anthropic endpoint would have sent a third-party token to
Anthropic. The on-disk Claude Code subscription token is Anthropic-only and never leaves it.
Credential precedence mirrors isolateClaudeRuntimeAuthEnv: for a bound session the ambient auth env
keys the spawn strips are ignored, keeping only ANTHROPIC_API_KEY for the anthropic service. The
catalog cache is keyed on resolved config dir + endpoint + a SHA-256 fingerprint of the credential
actually used, so a re-auth cannot inherit another account's list; a credential that cannot be read
bypasses the cache entirely rather than writing a placeholder entry that would evict a valid one.

Effort/ultracode. reasoningEffort is session-scoped and is not cleared when the model changes,
so an unrecognised model id is not evidence a carried level is supported. Tiers are resolved once when
the mode is built and travel on it as modelEffortLevels, so spawn resolution and launch-option
hashing see the same value and hashing stays a pure function of the mode. Requests clamp to those
tiers; with none, nothing is sent. Curated models keep their static table, so Haiku still never
receives --effort.

Effort applies from the first turn. An earlier revision resolved a model's tiers with a
fire-and-forget refresh while the message handler continued synchronously, so --effort and
ultracode were dropped for the first turn after any model change (and for a session whose model
never changed at all). Both user-message handlers now await the tier resolution before building the
mode. SessionClient already awaits its user-message callback, and resolveClaudeModelCatalog
caches, so this costs a no-op after the first resolve.

Pre-existing failures on dev, measured not assumed. yarn workspace @happier-dev/cli typecheck
fails on dev itself:

src/capabilities/deps/gh.releaseBinary.test.ts(33,3): error TS1005: ';' expected.

That file is byte-identical between this branch and dev. For tests, I ran src/backends/claude and
src/capabilities on this branch and again on a detached 4b76fc8c6: both produce the same 16
failing files, diff clean. None appear in this diff. That lane is also mildly flaky — three runs on
this branch gave 16, 17, 16 failing files, with the two captured file lists identical, so the stable
set is 16.

AI disclosure

Authored with Claude Code (Opus 5), reviewed by Codex (gpt-5.5) across four rounds and by CodeRabbit and Greptile on this PR. Codex found three
real defects on this branch — ambient credentials preempting a bound account, a stale-tier race across
the spawn path, and cache staleness on credential rotation. CodeRabbit found six more, including the first-turn effort gap above; all are fixed and covered by tests. Checks executed on af97ce02b, rebased onto 4b76fc8c6:

  • cd apps/cli && yarn typecheck — 1 error, pre-existing on dev (above)
  • cd apps/cli && yarn vitest run src/backends/claude src/capabilities — 16 failed / 308 passed, failing set identical to dev
  • cd apps/ui && yarn typecheck — 0 errors
  • cd apps/ui && yarn vitest run sources/sync/domains/models sources/components/sessions/pickers sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.refresh.test.tsx — 93 passed / 9 files

Not verified: no code on this branch was executed against the live Anthropic Models API, and no session
was spawned end-to-end. The GET /v1/models contract (200 over a subscription OAuth token,
capabilities.effort tiers, display_name, max_input_tokens) was confirmed by a manual spike before
this work; the fetch, merge, credential-routing and spawn paths are covered by unit tests with a mocked
fetch boundary only. The full apps/ui suite could not be run — it aborts with ERR_IPC_CHANNEL_CLOSED
in this environment, including with --no-file-parallelism; the targeted lanes above cover every UI
file this branch touches. Codex's reviews were static analysis only — its sandbox is read-only and
could not execute tests.

Checklist

  • PR targets dev (not main)
  • I linked an issue/discussion (recommended for non-trivial changes)
  • I added/updated tests where feasible (or explained why not)
  • I updated docs if behavior changed
  • If AI-assisted, I disclosed it and listed what I personally verified

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Note

Resolve Claude model list at runtime via the Anthropic API instead of static configuration

  • Replaces the static Claude model list with a dynamic catalog fetched from the Anthropic /v1/models API via resolveClaudeModelCatalog, with curated presentation metadata (name, description, extendedContextModelId) overlaid when a model matches a curated entry.
  • Adds probeClaudeInstalledRuntimeCapabilities to detect whether the installed Claude CLI supports --effort and ultracode, replacing the previous help-text string probing; model options are filtered per capability at probe time.
  • Introduces createClaudeModelEffortLevelsTracker to resolve and cache supported effort tiers per selected model with concurrency guards and a bounded 400ms wait before building initial launch modes.
  • Adds reconcileClaudeSessionModelsState to deterministically merge catalog- and Agent SDK-sourced model publications, preserving ordering and currentModelId rules per source.
  • The preflight probe adapter now declares modelProbeCachePolicy: 'provider-owned', bypassing generic cache storage and returning cacheable: false; probe cache keys are partitioned by profileId when supplied.
  • Propagates extendedContextModelId through the full pipeline: API parsing, catalog resolution, preflight probes, session metadata, and UI state.
  • Risk: mode hash values for Claude enhanced modes now incorporate supportedLevels, so existing queued or cached hashes will not match after this change.

Macroscope summarized fa239ff.

Summary by CodeRabbit

  • New Features

    • Claude model lists now support dynamic discovery, including capabilities, effort levels, context windows, and extended-context variants.
    • Reasoning effort and Ultracode options adapt to each model’s supported capabilities.
    • New-session model selection respects the active profile and refreshes profile-specific results.
    • Extended-context model identifiers are preserved across sessions and metadata.
  • Bug Fixes

    • Unsupported Claude options are hidden while sessions remain usable.
    • Improved model discovery caching, credential handling, fallback behavior, and stale-result protection.
  • Documentation

    • Documented dynamic model-list behavior and fallback rules.

Claude built its model list twice: the new-session preflight probe adapter and
the in-session sessionModelsV1 publisher, which rebuilt it from
AGENT_MODEL_CONFIG. Two producers of the same concept can disagree about which
models exist and which effort tiers they support.

Introduce one owner (backends/claude/models/resolveClaudeModelCatalog): the
curated catalog augmented with whatever the Anthropic Models API reports. Both
producers read it. Curated entries keep their hand-authored labels and effort
defaults, dated snapshot ids collapse onto their static alias, and models from a
generation below the curated floor are dropped — the Models API lists everything
the account may call, including generations Claude Code can no longer run. Any
failure falls back to the curated catalog. Results are cached per resolved
account config dir + endpoint + ambient-credential fingerprint, so a session
start does not pay a network round trip and a credential swap is not served a
previous account's list.

Credentials belong to the endpoint they are sent to. The catalog honors
ANTHROPIC_BASE_URL, because Happier's built-in Z.AI, DeepSeek, and MiniMax
Claude profiles point it at a gateway and pair it with a gateway-issued
ANTHROPIC_AUTH_TOKEN; the on-disk Claude Code subscription token is
Anthropic-only and is never sent to a third-party gateway. Credential precedence
mirrors isolateClaudeRuntimeAuthEnv: for a bound session the ambient auth env
keys the spawn would strip are ignored, keeping only ANTHROPIC_API_KEY for the
anthropic service. Reading a key the spawn deletes would describe one account for
a session that runs as another. The probe adapter is typed as a session controls
probe adapter — the shape the caller actually invokes — so it no longer drops
connectedServices, and the catalog entry partitions the probe cache by binding.

Effort and ultracode require evidence of support rather than trusting an
unrecognised model id. reasoningEffort is session-scoped and is not cleared when
the model changes, so an unknown id alone is not a reason to forward a carried
level. The selected model's reported tiers are resolved once when the mode is
built and travel on it as modelEffortLevels, so spawn-time resolution and
launch-option hashing see the same value and hashing stays a pure function of the
mode. The request is clamped to those tiers; with no tiers nothing is sent. A
discovered id that merely contains a curated alias uses its own reported tiers,
not the alias table. Curated models keep their static table, so Haiku still never
receives --effort or ultracode, and neither does a session with no model selected.

Claude stays on dynamicProbe: 'static-only'. Turning the app onto the dynamic
model path is a separate, user-visible change and lands on its own.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ec5725fb-2af0-4249-a129-dbbddc2026f6

📥 Commits

Reviewing files that changed from the base of the PR and between c4ef502 and fa239ff.

📒 Files selected for processing (3)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts
  • apps/cli/src/backends/claude/runClaude.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts
  • apps/cli/src/backends/claude/runClaude.ts

Walkthrough

Claude now discovers account-specific models from Anthropic, propagates model capabilities through runtime and session flows, supports profile-aware provider probing, and preserves extended-context model identifiers across metadata and UI model options.

Changes

Claude dynamic model discovery

Layer / File(s) Summary
Anthropic discovery and catalog resolution
apps/cli/src/backends/claude/models/*
Claude fetches authenticated model data, merges discovered models with curated entries, derives effort and context metadata, and caches account-specific results.
Probe, authentication, and cache integration
apps/cli/src/capabilities/probes/*, apps/cli/src/rpc/handlers/capabilities.ts, apps/ui/sources/components/sessions/new/*
Model probes receive profile and credential context. Claude uses provider-owned caching and dynamic probing with static fallback.
Model capability propagation
apps/cli/src/backends/claude/utils/*, apps/cli/src/backends/claude/runClaude.ts, apps/cli/src/backends/claude/remote/*, apps/cli/src/backends/claude/unifiedTerminal/*
Resolved effort tiers control CLI arguments, Ultracode, runtime configuration, queue hashes, and session startup behavior.
Session model reconciliation and publication
apps/cli/src/backends/claude/sessionControls/*, apps/cli/src/backends/claude/sessionModels/*, apps/cli/src/backends/claude/remote/buildClaudeSessionModelsMetadataFromSupportedModels.ts
Catalog and Agent SDK model publications are reconciled. Unsupported effort overrides are removed from metadata.
Extended-context metadata and model options
apps/cli/src/api/types.ts, apps/ui/sources/sync/domains/models/*, packages/agents/src/sessionControls/*
Extended-context identifiers are validated, normalized, persisted, merged, and exposed in model options.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NewSessionUI
  participant ProbeAgentModels
  participant ClaudePreflightModelsProbeAdapter
  participant ClaudeModelCatalog
  participant AnthropicModelsAPI
  NewSessionUI->>ProbeAgentModels: request models with profile context
  ProbeAgentModels->>ClaudePreflightModelsProbeAdapter: forward credentials and profile
  ClaudePreflightModelsProbeAdapter->>ClaudeModelCatalog: resolve account-specific catalog
  ClaudeModelCatalog->>AnthropicModelsAPI: fetch model capabilities
  AnthropicModelsAPI-->>ClaudeModelCatalog: return discovered models
  ClaudeModelCatalog-->>ClaudePreflightModelsProbeAdapter: return merged descriptors
  ClaudePreflightModelsProbeAdapter-->>ProbeAgentModels: return probe models
  ProbeAgentModels-->>NewSessionUI: return model options and metadata
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: resolving Claude models at runtime.
Description check ✅ Passed The description covers the required summary, rationale, testing, UI impact, notes, checklist, and AI disclosure sections in detail.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Claude model discovery now resolves the account-specific catalog at runtime and carries discovered model metadata through session startup and the UI.

  • Adds credential-aware Anthropic model fetching, caching, and static-catalog fallback behavior.
  • Propagates per-model effort tiers, context-window metadata, and extended-context identifiers through CLI and UI state.
  • Scopes preflight probing and cached results by profile while reconciling catalog and Agent SDK model publications.
  • Replaces the startup-mode regression test’s broad types with types derived from the loop signature.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up scope.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/cli/src/backends/claude/models/fetchAnthropicModels.ts Adds defensive Anthropic-compatible model fetching with credential-specific headers, timeout handling, redirect rejection, and response parsing.
apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts Centralizes credential-aware model resolution, cache isolation, static fallback, and curated metadata enrichment.
apps/cli/src/backends/claude/runClaude.ts Carries model-scoped effort evidence through both Claude runtime startup paths and awaits bounded resolution before message-mode construction.
apps/cli/src/backends/claude/sessionModels/reconcileClaudeSessionModelsState.ts Reconciles catalog and Agent SDK model publications into one session model state.
apps/ui/sources/sync/domains/models/modelOptions.ts Builds dynamic model rows while preserving discovered model metadata and model-specific options.
apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.ts Adds profile-aware model probing, persistence, refresh handling, and stale-result protection.
packages/agents/src/models.ts Extends the shared model descriptor contract with runtime-discovered model metadata.

Sequence Diagram

sequenceDiagram
  participant UI as New-session UI
  participant CLI as CLI model probe
  participant Catalog as Claude catalog resolver
  participant API as Anthropic Models API
  participant Session as Claude session

  UI->>CLI: Probe models for profile and workspace
  CLI->>Catalog: Resolve credential-aware catalog
  Catalog->>API: GET /v1/models
  alt Successful response
    API-->>Catalog: Account model descriptors
    Catalog-->>CLI: Dynamic models plus curated metadata
  else Missing credential or request failure
    Catalog-->>CLI: Curated fallback catalog
  end
  CLI-->>UI: Model list and per-model options
  UI->>Session: Start with selected model and options
  Session->>Catalog: Resolve effort tiers
  Catalog-->>Session: Model-scoped tier evidence
Loading

Reviews (9): Last reviewed commit: "test(claude): type startup mode regressi..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/cli/src/backends/claude/runClaude.ts (1)

963-975: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The first turn after a model change spawns without reported tiers.

Line 967 sets currentModel, then starts refreshCurrentModelEffortLevels without awaiting it. Line 851 clears currentModelEffortLevels at the transition. The handler continues synchronously and builds enhancedMode at Lines 1046-1059 with the now-empty currentModelEffortLevels.

For a discovered (non-curated) model, resolveEvidencedClaudeEffortLevels then finds no evidence. --effort and ultracode are dropped for that turn, and hashClaudeEnhancedModeForQueue hashes effort: null. The user-selected effort silently does not apply until a later message. The same sequence exists in the fast-start path at Lines 1894 and 1966.

resolveClaudeModelCatalog caches results, so awaiting the refresh before building enhancedMode is normally cheap. Consider making the user-message handler await the refresh (or resolve tiers at push time) so the selected effort applies on the first turn.

#!/bin/bash
# Check the catalog cache and timeout semantics before deciding to await the refresh.
fd -t f 'resolveClaudeModelCatalog.ts' apps/cli/src | xargs -r rg -n -C6 'cache|timeoutMs|export async function|export function'

Also applies to: 1046-1059

🤖 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 `@apps/cli/src/backends/claude/runClaude.ts` around lines 963 - 975, Ensure the
user-message handling path awaits completion of refreshCurrentModelEffortLevels
after updating currentModel and before constructing enhancedMode, so the first
turn uses the refreshed effort tiers. Apply the same sequencing fix in the
fast-start path around its model update and enhancedMode construction,
preserving existing model-reset and timestamp behavior.
🧹 Nitpick comments (7)
apps/cli/src/backends/claude/preflight/anthropicModelsFetch.ts (1)

64-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Compute the parsed values once.

readCapabilities calls readEffortTier twice per tier, and line 106 calls readCapabilities twice for the same entry. The results are pure, so a single call per value is enough and reads more clearly.

♻️ Proposed refactor
 function readCapabilities(value: unknown): AnthropicModelCapabilities | undefined {
   const caps = readObject(value);
   const effort = readObject(caps?.effort);
   if (!effort) return undefined;
+  const tiers = {
+    low: readEffortTier(effort.low),
+    medium: readEffortTier(effort.medium),
+    high: readEffortTier(effort.high),
+    xhigh: readEffortTier(effort.xhigh),
+    max: readEffortTier(effort.max),
+  } as const;
   return {
     effort: {
       ...(typeof effort.supported === 'boolean' ? { supported: effort.supported } : {}),
-      ...(readEffortTier(effort.low) ? { low: readEffortTier(effort.low) } : {}),
-      ...(readEffortTier(effort.medium) ? { medium: readEffortTier(effort.medium) } : {}),
-      ...(readEffortTier(effort.high) ? { high: readEffortTier(effort.high) } : {}),
-      ...(readEffortTier(effort.xhigh) ? { xhigh: readEffortTier(effort.xhigh) } : {}),
-      ...(readEffortTier(effort.max) ? { max: readEffortTier(effort.max) } : {}),
+      ...Object.fromEntries(Object.entries(tiers).filter(([, tier]) => tier !== undefined)),
     },
   };
 }
+    const capabilities = readCapabilities(entry?.capabilities);
     entries.push({
       id,
       ...(displayName ? { displayName } : {}),
       ...(maxInputTokens !== undefined ? { maxInputTokens } : {}),
-      ...(readCapabilities(entry?.capabilities) ? { capabilities: readCapabilities(entry?.capabilities) } : {}),
+      ...(capabilities ? { capabilities } : {}),
     });

Also applies to: 102-107

🤖 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 `@apps/cli/src/backends/claude/preflight/anthropicModelsFetch.ts` around lines
64 - 78, Update readCapabilities to call readEffortTier once for each effort
tier, store each parsed result, and reuse it when constructing the returned
capabilities. In the entry-processing flow around readCapabilities, call
readCapabilities once per entry and reuse its result instead of invoking it
twice.
apps/cli/src/backends/claude/preflight/deriveDiscoveredClaudeModel.ts (1)

8-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the Ultracode copy with the static catalog instead of duplicating it.

The comment states this string mirrors withClaudeEffortModelOptions in the agents package. Two copies will drift. Export the description from the canonical Claude provider module in packages/agents and import it here.

As per coding guidelines: "Reuse or extend canonical implementations instead of adding similar-but-different logic."

#!/bin/bash
# Locate the curated Ultracode option copy and check whether it is already exported.
rg -nP -C4 'Ultracode|ultracode' packages/agents/src | head -80
rg -nP -C6 'withClaudeEffortModelOptions' packages/agents/src
🤖 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 `@apps/cli/src/backends/claude/preflight/deriveDiscoveredClaudeModel.ts` around
lines 8 - 11, Remove the local ULTRACODE_DESCRIPTION constant and export the
canonical Ultracode description from the Claude provider module in
packages/agents alongside withClaudeEffortModelOptions. Import and reuse that
exported symbol in deriveDiscoveredClaudeModel so discovered and curated models
share one source of truth.
apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts (1)

272-321: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider de-duplicating concurrent resolutions.

The preflight probe and the sessionModelsV1 publisher can both resolve the catalog at session start. Both miss the cache and both issue a network fetch for the same key. Store the in-flight promise in the map so the second caller awaits the first. Pruning expired entries during that write also keeps catalogCache from growing for every rotated credential.

♻️ Sketch
-type CatalogCacheEntry = Readonly<{ models: readonly AgentModelDescriptor[]; expiresAtMs: number }>;
-const catalogCache = new Map<string, CatalogCacheEntry>();
+type CatalogCacheEntry = Readonly<{ models: readonly AgentModelDescriptor[]; expiresAtMs: number }>;
+const catalogCache = new Map<string, CatalogCacheEntry>();
+const inFlightCatalogFetches = new Map<string, Promise<readonly AgentModelDescriptor[]>>();

Then wrap the fetch-and-store block in a promise stored under cacheKey, and delete it in a finally.

🤖 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 `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts` around
lines 272 - 321, Update resolveClaudeModelCatalog and the catalogCache flow to
track in-flight resolutions per cacheKey so concurrent callers await one fetch
instead of issuing duplicate network requests. Store the fetch-and-cache promise
before awaiting it, remove it in finally, and preserve existing success/failure
TTL behavior. During writes, prune expired cache entries so rotated credentials
do not grow catalogCache indefinitely.
apps/ui/sources/sync/domains/models/modelOptions.ts (1)

153-155: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract one reader for extendedContextModelId. Four sites now repeat the same "string, non-blank, trimmed" normalization. Add a single helper in the models domain (for example readExtendedContextModelId(value: unknown): string | undefined) and call it from each site, so the normalization rule has one owner.

  • apps/ui/sources/sync/domains/models/modelOptions.ts#L153-L155: replace the inline guard in getModelOptionsForPreflightModelList with the helper, and export the helper from this module.
  • apps/ui/sources/sync/domains/models/modelOptions.ts#L287-L289: replace the inline guard in resolveModelOptionsForSession with the helper.
  • apps/ui/sources/sync/domains/models/parsePreflightModelListFromProbeModelsResult.ts#L18-L20: replace the inline guard with the helper.
  • apps/ui/sources/sync/domains/models/dynamicModelProbeCache.ts#L50-L52: replace the inline guard with the helper.

As per coding guidelines: "If similar logic already exists, extend or extract the canonical owner instead of creating a second path."

🤖 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 `@apps/ui/sources/sync/domains/models/modelOptions.ts` around lines 153 - 155,
Extract and export a canonical readExtendedContextModelId helper that returns a
trimmed string only for string, non-blank inputs, otherwise undefined. Update
getModelOptionsForPreflightModelList and resolveModelOptionsForSession in
apps/ui/sources/sync/domains/models/modelOptions.ts (anchor 153-155 and sibling
287-289), plus
apps/ui/sources/sync/domains/models/parsePreflightModelListFromProbeModelsResult.ts
(18-20) and apps/ui/sources/sync/domains/models/dynamicModelProbeCache.ts
(50-52), to use this helper instead of their inline normalization guards.

Source: Coding guidelines

apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts (1)

17-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Spread the original module in the credential-file mock.

This factory replaces the whole module with one export. If any module in the import graph starts to use another export of claudeCodeCredentialFile, this suite fails for an unrelated reason. The anthropicModelsFetch mock above already uses importOriginal. Use the same pattern here.

♻️ Proposed change
-vi.mock('`@/backends/claude/connectedServices/nativeAuth/claudeCodeCredentialFile`', () => ({
-  readClaudeCodeNativeCredential: readClaudeCodeNativeCredentialMock,
-}));
+vi.mock('`@/backends/claude/connectedServices/nativeAuth/claudeCodeCredentialFile`', async (importOriginal) => {
+  const actual = await importOriginal<
+    typeof import('`@/backends/claude/connectedServices/nativeAuth/claudeCodeCredentialFile`')
+  >();
+  return { ...actual, readClaudeCodeNativeCredential: readClaudeCodeNativeCredentialMock };
+});
🤖 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 `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts` around
lines 17 - 19, Update the claudeCodeCredentialFile mock factory around
readClaudeCodeNativeCredentialMock to import and spread the original module
exports, overriding only readClaudeCodeNativeCredential. Preserve all other
exports so future imports do not break this test suite.
apps/cli/src/capabilities/probes/resolveAgentProbeVariant.test.ts (1)

5-52: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Assert variant stability for an equivalent binding.

The current assertions only prove separation. A variant implementation that generates a new value on every call would pass these tests and disable cache reuse. Resolve the same profile binding twice and assert that both values are equal.

Before retaining this new suite, inventory existing resolveAgentProbeVariant coverage and consolidate overlapping cases.

As per coding guidelines, “Assert observable behavior and stable contracts” and “Do a test inventory before adding tests.”

🤖 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 `@apps/cli/src/capabilities/probes/resolveAgentProbeVariant.test.ts` around
lines 5 - 52, Update the resolveAgentProbeVariant test suite to first
consolidate overlapping coverage with existing tests, then add an assertion that
resolving the identical Claude profile binding twice returns equal variants,
preserving cache reuse while retaining distinct variants for different bindings.

Source: Coding guidelines

apps/cli/src/backends/claude/runClaude.ts (1)

1513-1542: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Declare resolveClaudeHelpProbeTimeoutMs before this closure.

Line 1533 reads resolveClaudeHelpProbeTimeoutMs, but that const is declared at Line 1557. The current call sites (Lines 1822, 1894, 2151) all run inside later callbacks, so the binding is initialized by then. A future direct call during startup would throw a ReferenceError from the temporal dead zone.

Move the resolveClaudeHelpProbeTimeoutMs declaration above refreshCurrentModelEffortLevels. The extraction suggested for Lines 833-868 also removes this ordering hazard.

🤖 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 `@apps/cli/src/backends/claude/runClaude.ts` around lines 1513 - 1542, Move the
const declaration for resolveClaudeHelpProbeTimeoutMs above the
refreshCurrentModelEffortLevels closure so the closure never references it
before initialization. Preserve its existing implementation and behavior while
removing the temporal-dead-zone ordering hazard.
🤖 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 `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts`:
- Around line 102-110: Update isAnthropicFirstPartyBaseUrl to require the parsed
URL protocol to be HTTPS in addition to matching anthropic.com or its
subdomains. Ensure HTTP URLs return false, while preserving the existing
handling for null and invalid URLs.

In `@apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts`:
- Around line 696-710: Ensure model-specific effort tiers are used only for the
model they describe: in
apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts (696-710), pass
supportedLevels only when effortModelId equals mode.model; in
apps/cli/src/backends/claude/cli/terminalOptions.ts (192-196), apply the
equivalent effectiveModel versus normalized mode.model guard; in
apps/cli/src/backends/claude/cli/terminalOptions.ts (230-238), apply the same
guard to resolveClaudeUltracodeForModel; and in
apps/cli/src/backends/claude/claudeRemote.ts (228-232), pass
mode.modelEffortLevels only when argOverrides.model is absent or matches
initial.mode.model.

In `@apps/cli/src/backends/claude/runClaude.ts`:
- Around line 833-868: Extract the duplicated model-effort tracking logic from
the current refresh function and its counterpart near the second path into a
shared createClaudeModelEffortLevelsTracker({ resolveTimeoutMs }) factory
returning refresh and getLevels, then use that tracker in both callers while
preserving normalization, transition clearing, curated-model handling, and
stale-resolution guards. In the catalog lookup catch, add logger.debug with the
model and failure details so lookup failures are visible without changing
fallback behavior.

In
`@apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts`:
- Around line 20-25: Update the session model resolution flow around
resolveClaudeModelCatalog so catalog publication no longer returns early when
--effort is unsupported. Always resolve and publish discovered models, while
suppressing only the effort option when probeHelpText lacks --effort; add a
regression test covering a discovered model under that probe condition.

In `@apps/cli/src/backends/claude/utils/claudeEffort.ts`:
- Around line 49-63: Restrict the static effort-level fallback in
resolveEvidencedClaudeEffortLevels to curated model IDs, so non-curated
discovered IDs with no reported tiers return no levels even when
resolveClaudeEffortLevelsForKnownAliasOrModel matches a substring alias.
Preserve reported-tier precedence and curated-model behavior, verify bare
aliases remain supported as intended, and add coverage for a discovered
substring-matching ID such as claude-opus-5-preview with empty supportedLevels.

---

Outside diff comments:
In `@apps/cli/src/backends/claude/runClaude.ts`:
- Around line 963-975: Ensure the user-message handling path awaits completion
of refreshCurrentModelEffortLevels after updating currentModel and before
constructing enhancedMode, so the first turn uses the refreshed effort tiers.
Apply the same sequencing fix in the fast-start path around its model update and
enhancedMode construction, preserving existing model-reset and timestamp
behavior.

---

Nitpick comments:
In `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts`:
- Around line 17-19: Update the claudeCodeCredentialFile mock factory around
readClaudeCodeNativeCredentialMock to import and spread the original module
exports, overriding only readClaudeCodeNativeCredential. Preserve all other
exports so future imports do not break this test suite.

In `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts`:
- Around line 272-321: Update resolveClaudeModelCatalog and the catalogCache
flow to track in-flight resolutions per cacheKey so concurrent callers await one
fetch instead of issuing duplicate network requests. Store the fetch-and-cache
promise before awaiting it, remove it in finally, and preserve existing
success/failure TTL behavior. During writes, prune expired cache entries so
rotated credentials do not grow catalogCache indefinitely.

In `@apps/cli/src/backends/claude/preflight/anthropicModelsFetch.ts`:
- Around line 64-78: Update readCapabilities to call readEffortTier once for
each effort tier, store each parsed result, and reuse it when constructing the
returned capabilities. In the entry-processing flow around readCapabilities,
call readCapabilities once per entry and reuse its result instead of invoking it
twice.

In `@apps/cli/src/backends/claude/preflight/deriveDiscoveredClaudeModel.ts`:
- Around line 8-11: Remove the local ULTRACODE_DESCRIPTION constant and export
the canonical Ultracode description from the Claude provider module in
packages/agents alongside withClaudeEffortModelOptions. Import and reuse that
exported symbol in deriveDiscoveredClaudeModel so discovered and curated models
share one source of truth.

In `@apps/cli/src/backends/claude/runClaude.ts`:
- Around line 1513-1542: Move the const declaration for
resolveClaudeHelpProbeTimeoutMs above the refreshCurrentModelEffortLevels
closure so the closure never references it before initialization. Preserve its
existing implementation and behavior while removing the temporal-dead-zone
ordering hazard.

In `@apps/cli/src/capabilities/probes/resolveAgentProbeVariant.test.ts`:
- Around line 5-52: Update the resolveAgentProbeVariant test suite to first
consolidate overlapping coverage with existing tests, then add an assertion that
resolving the identical Claude profile binding twice returns equal variants,
preserving cache reuse while retaining distinct variants for different bindings.

In `@apps/ui/sources/sync/domains/models/modelOptions.ts`:
- Around line 153-155: Extract and export a canonical readExtendedContextModelId
helper that returns a trimmed string only for string, non-blank inputs,
otherwise undefined. Update getModelOptionsForPreflightModelList and
resolveModelOptionsForSession in
apps/ui/sources/sync/domains/models/modelOptions.ts (anchor 153-155 and sibling
287-289), plus
apps/ui/sources/sync/domains/models/parsePreflightModelListFromProbeModelsResult.ts
(18-20) and apps/ui/sources/sync/domains/models/dynamicModelProbeCache.ts
(50-52), to use this helper instead of their inline normalization guards.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c85e2571-7b76-4ac6-bd60-cebe1ee7eca8

📥 Commits

Reviewing files that changed from the base of the PR and between 7f063b0 and dc3343a.

📒 Files selected for processing (31)
  • apps/cli/src/backends/claude/claudeRemote.ts
  • apps/cli/src/backends/claude/cli/terminalOptions.ts
  • apps/cli/src/backends/claude/connectedServices/resolveClaudeConnectedServiceStableAuthDir.ts
  • apps/cli/src/backends/claude/index.ts
  • apps/cli/src/backends/claude/loop.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/preflight/anthropicModelsFetch.test.ts
  • apps/cli/src/backends/claude/preflight/anthropicModelsFetch.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.ts
  • apps/cli/src/backends/claude/preflight/deriveDiscoveredClaudeModel.test.ts
  • apps/cli/src/backends/claude/preflight/deriveDiscoveredClaudeModel.ts
  • apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts
  • apps/cli/src/backends/claude/remote/modeHash.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts
  • apps/cli/src/backends/claude/unifiedTerminal/runtimeControlIntegration.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.test.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.staticOnly.test.ts
  • apps/cli/src/capabilities/probes/resolveAgentProbeVariant.test.ts
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.refresh.test.tsx
  • apps/ui/sources/sync/domains/models/dynamicModelProbeCache.ts
  • apps/ui/sources/sync/domains/models/modelOptions.test.ts
  • apps/ui/sources/sync/domains/models/modelOptions.ts
  • apps/ui/sources/sync/domains/models/parsePreflightModelListFromProbeModelsResult.ts
  • apps/ui/sources/sync/domains/state/storageTypes.ts
  • docs/agents-catalog.md
  • packages/agents/src/index.ts
  • packages/agents/src/models.ts
💤 Files with no reviewable changes (1)
  • apps/cli/src/capabilities/probes/agentModelsProbe.staticOnly.test.ts

Comment thread apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts Outdated
Comment thread apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts
Comment thread apps/cli/src/backends/claude/runClaude.ts Outdated
Comment thread apps/cli/src/backends/claude/utils/claudeEffort.ts
@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from dc3343a to fcd3a1c Compare August 10, 2026 21:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/cli/src/backends/claude/utils/claudeEffort.ts (1)

159-166: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use exported arrow functions for the new CLI helpers.

  • apps/cli/src/backends/claude/utils/claudeEffort.ts#L159-L166: convert resolveModeEffortLevelsForModel to an exported const arrow function.
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts#L25-L68: convert createClaudeModelEffortLevelsTracker to an exported const arrow function.

As per coding guidelines: “Prefer arrow functions over function declarations.”

🤖 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 `@apps/cli/src/backends/claude/utils/claudeEffort.ts` around lines 159 - 166,
Convert resolveModeEffortLevelsForModel in
apps/cli/src/backends/claude/utils/claudeEffort.ts:159-166 to an exported const
arrow function, preserving its parameters and behavior. Also convert
createClaudeModelEffortLevelsTracker in
apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts:25-68 to
an exported const arrow function, preserving its existing implementation and
API.

Source: Coding guidelines

🤖 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 `@apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts`:
- Around line 25-68: The exported createClaudeModelEffortLevelsTracker needs
focused unit coverage. Add tests using the existing Claude model test helpers
for blank-model reset, curated-model lookup bypass, catalog-resolution failure,
and a late catalog result after refresh changes the model; assert that stale or
failed lookups do not publish effort levels for the current model.

---

Nitpick comments:
In `@apps/cli/src/backends/claude/utils/claudeEffort.ts`:
- Around line 159-166: Convert resolveModeEffortLevelsForModel in
apps/cli/src/backends/claude/utils/claudeEffort.ts:159-166 to an exported const
arrow function, preserving its parameters and behavior. Also convert
createClaudeModelEffortLevelsTracker in
apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts:25-68 to
an exported const arrow function, preserving its existing implementation and
API.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c3553b1-37dd-47cd-868f-05b76a05dba1

📥 Commits

Reviewing files that changed from the base of the PR and between dc3343a and fcd3a1c.

📒 Files selected for processing (13)
  • apps/cli/src/backends/claude/claudeRemote.ts
  • apps/cli/src/backends/claude/cli/terminalOptions.ts
  • apps/cli/src/backends/claude/loop.ts
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts
  • apps/cli/src/backends/claude/remote/modeHash.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.test.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts
  • apps/cli/src/backends/claude/unifiedTerminal/runtimeControlIntegration.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.test.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/cli/src/backends/claude/utils/claudeEffort.test.ts
  • apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts
  • apps/cli/src/backends/claude/claudeRemote.ts
  • apps/cli/src/backends/claude/unifiedTerminal/runtimeControlIntegration.ts
  • apps/cli/src/backends/claude/cli/terminalOptions.ts
  • apps/cli/src/backends/claude/remote/modeHash.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/runClaude.ts

@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from fcd3a1c to b5f85d7 Compare August 10, 2026 22:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts`:
- Around line 125-129: Add an explicit assertion that releaseFirst is available
before invoking it in the first lookup setup, and fail with a clear error when
it is null. Keep the subsequent await first only after this validation so the
test does not rely on the timeout.
- Around line 33-44: Update the createTracker helper to explicitly return
ReturnType<typeof createClaudeModelEffortLevelsTracker>, and convert both
effortCapabilities and createTracker to arrow functions while preserving their
behavior. Run the CLI workspace typecheck to verify the changes.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d62db3e-3be3-470d-823e-5657f227d5cc

📥 Commits

Reviewing files that changed from the base of the PR and between fcd3a1c and b5f85d7.

📒 Files selected for processing (1)
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts

Comment thread apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts Outdated
Comment thread apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts Outdated
@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from b5f85d7 to 2c5ca81 Compare August 11, 2026 08:28
@danljungstrom

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

All eight threads from the previous rounds are addressed and answered as of 2c5ca81. The
outstanding CHANGES_REQUESTED was submitted against fcd3a1c, two pushes ago, so a fresh pass on
the current head would be useful.

Summary of what changed since that review:

  • isAnthropicFirstPartyBaseUrl requires https: before trusting a host as first-party.
  • Effort tiers are paired with modelEffortLevelsModelId and only applied to the model actually
    being launched, via resolveModeEffortLevelsForModel.
  • The duplicated effort-tier trackers were extracted into createClaudeModelEffortLevelsTracker,
    now with direct unit coverage including the late-lookup race.
  • sessionModelsV1 publication no longer depends on --effort support; only the effort and
    ultracode options are suppressed when the CLI cannot apply them.
  • A discovered model id no longer inherits curated tiers through a substring alias match.
  • Test-quality fixes on the new tracker tests: declared return type, arrow helpers, and a hard
    failure instead of a silent skip when the deferred lookup never starts.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

@danljungstrom I will perform a full review of the current head of #237. I will evaluate the updated implementation and the changes since the previous review.

⚠️ Action not completed

Head commit changed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts`:
- Around line 53-56: Update the test suite’s afterEach cleanup to reset the
shared catalogCache after restoring and recreating envScope, ensuring no cached
model catalog remains available to later tests sharing the module context.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6da88220-e46e-44e5-80d6-8861d2c7470a

📥 Commits

Reviewing files that changed from the base of the PR and between b5f85d7 and 2c5ca81.

📒 Files selected for processing (1)
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts

@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from 2c5ca81 to cbaa550 Compare August 11, 2026 08:34
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 11, 2026
@danljungstrom

Copy link
Copy Markdown
Contributor Author

@coderabbitai on the outside-diff finding at apps/cli/src/backends/claude/runClaude.ts#L963-L975
("The first turn after a model change spawns without reported tiers") — confirmed and fixed in
af97ce0.

You were right on both counts, including the part I had reasoned my way out of. I had documented this
in the PR body as a deliberate limitation, on the grounds that awaiting would mean either making
session.onUserMessage async — a contract I assumed I would be guessing at — or putting a network
call on session start. Neither held up:

  • SessionClient already awaits its user-message callback (sessionClient.ts:3010,
    await this.pendingMessageCallback(...)) and the signature already accepts
    unknown | Promise<unknown>, so an async handler cannot reorder delivery.
  • resolveClaudeModelCatalog caches, exactly as you noted, so the await is a no-op after the first
    resolve.

Both user-message handlers are now async and await modelEffortTracker.refresh(currentModel)
immediately before building enhancedMode. The four fire-and-forget calls at the model-change sites
are gone, since the pre-build await supersedes them and refresh no-ops on an unchanged model.

One case your report did not cover, now also fixed: a session whose model never changes never hit a
refresh inside the handler at all, so it depended entirely on the session-ready call landing in time.
Awaiting before the mode build covers that too.

Verification: 67 passed across the models, effort, modeHash and startup-ordering lanes;
src/backends/claude src/capabilities is back to 16 failing files, byte-identical to a detached
4b76fc8c6. The PR body's "Known limitation" paragraph has been replaced — it described an
assumption, not a decision.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@danljungstrom Confirmed. The awaited refresh before each enhancedMode build addresses the model-change case and the unchanged-model startup case.

Thank you for validating the SessionClient callback contract and for replacing the documented assumption. The reported targeted test results and baseline comparison are sufficient for this follow-up.

🐇 ✅


✏️ Learnings added
Learnt from: danljungstrom
URL: https://github.com/happier-dev/happier/pull/237

Timestamp: 2026-08-11T13:04:30.585Z
Learning: In `apps/cli`, `SessionClient` awaits its user-message callback. The callback accepts `unknown | Promise<unknown>`, so Claude session handlers can be `async` without reordering message delivery.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from af97ce0 to 5cb244e Compare August 11, 2026 13:14
Flip Claude to dynamicProbe: 'auto' so the app runs the preflight models probe
and consumes the sessionModelsV1 list the CLI already publishes.

The flip switches the app from the static row builder to the dynamic one, which
carried less per-model metadata: `extendedContextModelId` was dropped by the
persisted session-model schema, the preflight parse, the probe cache, and both
dynamic row builders. AgentInput gates the 1M-context toggle on that field, so
without this the toggle would disappear for Claude and a session already on
claude-sonnet-4-6[1m] would lose its model controls.

Carry the field through every hop, and restore it from the catalog in
mergeDynamicModelOptionWithCatalog so a curated model arriving through the
dynamic path keeps its extended-context variant even when the dynamic source has
no reason to know about it. A dynamic source that declares its own variant is
honored too. `extendedContextModelId` is the only ModelOption field the dynamic
path was missing; the rest (label, description, modelOptions) already flowed or
were backfilled from the catalog.

Still open, tracked separately and not user-blocking for the list itself:
- modelEffortLevels is resolved once against options.modelId, so switching model
  mid-session keeps the previous model's tiers;
- the first turn builds its mode before the catalog resolves, so the first spawn
  of a discovered-model session carries no tiers.
@danljungstrom
danljungstrom force-pushed the feat/claude-dynamic-model-list branch from 5cb244e to ba5645d Compare August 11, 2026 16:06
@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@leeroybrun

Copy link
Copy Markdown
Collaborator

@greptileai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts (1)

123-130: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add an explicit return type to runProbe.

The other helpers in this file declare return types. runProbe does not.

♻️ Proposed change
-async function runProbe() {
+async function runProbe(): Promise<Array<Record<string, unknown>> | null> {
   return claudePreflightModelsProbeAdapter.probeModelsRaw?.({
     cwd: '/tmp',
     timeoutMs: 1_500,
     backendTarget: undefined,
     accountSettings: null,
   }) as Promise<Array<Record<string, unknown>> | null>;
 }

As per path instructions: "Always use types for function parameters and return values in TypeScript".

🤖 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
`@apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts`
around lines 123 - 130, Update the runProbe helper to declare an explicit
Promise return type matching the existing asserted result: an array of records
or null. Keep its current probeModelsRaw invocation and behavior unchanged.

Source: Path instructions

apps/cli/src/capabilities/probes/agentModelsProbe.ts (1)

464-466: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the guarded cache writes into one helper.

The pattern if (!usesProviderOwnedCache) { agentModelsProbeCache.setSuccess(...) } is repeated at nine sites. A future branch that writes to the cache can omit the guard and leak a provider-owned result into the shared cache. One helper closes that gap and shortens each branch.

♻️ Proposed refactor
+    const cacheSuccess = (value: ProbedAgentModelsResult, ttlMs: number): void => {
+      if (usesProviderOwnedCache) return;
+      agentModelsProbeCache.setSuccess(cacheKey, value, { nowMs: nowMs2, ttlMs });
+    };
+    const cacheError = (ttlMs: number): void => {
+      if (usesProviderOwnedCache) return;
+      agentModelsProbeCache.setError(cacheKey, { nowMs: nowMs2, ttlMs });
+    };

Each site then becomes a single call, for example:

-      if (!usesProviderOwnedCache) {
-        agentModelsProbeCache.setSuccess(cacheKey, fallback, { nowMs: nowMs2, ttlMs: PROBE_MODELS_SUCCESS_TTL_MS });
-      }
+      cacheSuccess(fallback, PROBE_MODELS_SUCCESS_TTL_MS);
       return fallback;

Also applies to: 485-499, 528-538, 554-572, 598-613

🤖 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 `@apps/cli/src/capabilities/probes/agentModelsProbe.ts` around lines 464 - 466,
Extract the repeated usesProviderOwnedCache guard and
agentModelsProbeCache.setSuccess call into a helper near the existing probe
logic, with parameters for the cache key, value, and timing/options. Replace all
nine guarded cache-write sites, including the fallback write in the shown
branch, with calls to this helper so every shared-cache success write
consistently enforces the provider-owned-cache exclusion.
🤖 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 `@apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts`:
- Around line 79-92: Update mergeStaticWithDiscovered to track normalized IDs
accepted from entries and filter out subsequent duplicates before
buildDiscoveredClaudeModelDescriptor runs. Retain one canonical discovered entry
when an alias and dated snapshot normalize to the same ID, while preserving
static-model filtering. Add coverage for both entries being returned together
and producing a single discovered row.

In `@apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts`:
- Around line 77-95: Update normalizeExplicitBaseUrl to reject any explicit URL
whose protocol is not https:, including http:, by returning 'invalid' before
accepting the resolved endpoint. Add a regression test covering an HTTP
ANTHROPIC_BASE_URL and assert the model-discovery resolution returns null.

In `@apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts`:
- Around line 536-602: Update the test around runClaude to exercise the
fast-start branch by using terminal/local startup settings that make
shouldUseFastStart true, while preserving the deferred catalog readiness
assertions. If retaining the existing remote setup, rename and narrow the test
to explicitly cover only standard-runner readiness, and add a separate
fast-start case with equivalent assertions.

In `@apps/cli/src/backends/claude/runClaude.ts`:
- Around line 2055-2056: Synchronize the fast-start model state in the override
callback that updates options.model by also updating currentModel and
currentModelUpdatedAt. Ensure the initial launch and model-specific effort
resolution use the persisted override rather than stale session state, and add
coverage for a resumed session with a persisted model override.

In
`@apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts`:
- Around line 65-76: Update the supportsEffort logic in
publishClaudeSessionModelsMetadataBestEffort to inspect only the availableModels
entry matching state.currentModelId, derive that model’s supported option IDs,
and remove every effort-dependent override not advertised by the selected model,
including reasoning_effort and ultracode. Preserve the existing metadata
reconciliation flow and add regression coverage for mixed-capability model
selections.

In `@docs/agents-catalog.md`:
- Around line 162-168: The documentation paragraph must accurately describe
Claude model-catalog caching: state that only a warm cache entry lets session
start avoid another network round trip, and revise the cache identity to use the
endpoint, credential kind, and SHA-256 hash of the resolved credential value.
Remove the claim that it includes the resolved account config directory or is
limited to ambient-credential fingerprints.

---

Nitpick comments:
In
`@apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts`:
- Around line 123-130: Update the runProbe helper to declare an explicit Promise
return type matching the existing asserted result: an array of records or null.
Keep its current probeModelsRaw invocation and behavior unchanged.

In `@apps/cli/src/capabilities/probes/agentModelsProbe.ts`:
- Around line 464-466: Extract the repeated usesProviderOwnedCache guard and
agentModelsProbeCache.setSuccess call into a helper near the existing probe
logic, with parameters for the cache key, value, and timing/options. Replace all
nine guarded cache-write sites, including the fallback write in the shown
branch, with calls to this helper so every shared-cache success write
consistently enforces the provider-owned-cache exclusion.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f953d4cc-8c54-4ff3-b174-c36dce649214

📥 Commits

Reviewing files that changed from the base of the PR and between 286810e and 86f1b51.

📒 Files selected for processing (61)
  • apps/cli/src/api/types.ts
  • apps/cli/src/backends/catalog.test.ts
  • apps/cli/src/backends/claude/claudeRemote.ts
  • apps/cli/src/backends/claude/cli/terminalOptions.ts
  • apps/cli/src/backends/claude/connectedServices/resolveClaudeConnectedServiceStableAuthDir.ts
  • apps/cli/src/backends/claude/index.ts
  • apps/cli/src/backends/claude/loop.ts
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.test.ts
  • apps/cli/src/backends/claude/models/claudeModelEffortLevelsTracker.ts
  • apps/cli/src/backends/claude/models/deriveDiscoveredClaudeModel.test.ts
  • apps/cli/src/backends/claude/models/deriveDiscoveredClaudeModel.ts
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.test.ts
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.ts
  • apps/cli/src/backends/claude/remote/buildClaudeSessionModelsMetadataFromSupportedModels.ts
  • apps/cli/src/backends/claude/remote/claudeRemoteAgentSdk.ts
  • apps/cli/src/backends/claude/remote/modeHash.ts
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.test.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.test.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts
  • apps/cli/src/backends/claude/sessionModels/reconcileClaudeSessionModelsState.ts
  • apps/cli/src/backends/claude/unifiedTerminal/dialogChoice/injectionDialogRouting.test.ts
  • apps/cli/src/backends/claude/unifiedTerminal/runtimeControlIntegration.test.ts
  • apps/cli/src/backends/claude/unifiedTerminal/runtimeControlIntegration.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.test.ts
  • apps/cli/src/backends/claude/utils/claudeEffort.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.cache.test.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.staticOnly.test.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.ts
  • apps/cli/src/capabilities/probes/preflightSessionControlsProbeAdapterTypes.ts
  • apps/cli/src/capabilities/probes/resolveAgentProbeVariant.test.ts
  • apps/cli/src/rpc/handlers/capabilities.probeModels.cwd.test.ts
  • apps/cli/src/rpc/handlers/capabilities.ts
  • apps/ui/sources/components/sessions/new/components/NewSessionEngineOptionDetail.tsx
  • apps/ui/sources/components/sessions/new/components/NewSessionFavoriteModelsDetail.tsx
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionAgentPickerControls.tsx
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.cwd.test.tsx
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.persistence.test.tsx
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.refresh.test.tsx
  • apps/ui/sources/components/sessions/new/hooks/screenModel/useNewSessionPreflightModelsState.ts
  • apps/ui/sources/components/sessions/new/hooks/useNewSessionScreenModel.tsx
  • apps/ui/sources/sync/domains/models/dynamicModelProbeCache.ts
  • apps/ui/sources/sync/domains/models/modelOptions.test.ts
  • apps/ui/sources/sync/domains/models/modelOptions.ts
  • apps/ui/sources/sync/domains/models/parsePreflightModelListFromProbeModelsResult.ts
  • apps/ui/sources/sync/domains/sessionControl/readSessionControlMetadata.test.ts
  • apps/ui/sources/sync/domains/sessionControl/schema.ts
  • apps/ui/sources/sync/domains/state/storageTypes.ts
  • docs/agents-catalog.md
  • packages/agents/src/index.ts
  • packages/agents/src/models.ts
  • packages/agents/src/sessionControls/metadata.spec.ts
  • packages/agents/src/sessionControls/metadata.ts

Comment thread apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts Outdated
Comment thread apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts Outdated
Comment thread apps/cli/src/backends/claude/runClaude.ts Outdated
Comment thread docs/agents-catalog.md
@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@greptile-apps please review the current head 599812c.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

@leeroybrun I will review the current head of #237.


🧠 Learnings used
Learnt from: leeroybrun
Repo: happier-dev/happier PR: 237
File: apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts:77-95
Timestamp: 2026-08-11T23:39:07.782Z
Learning: For Claude model discovery in `apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts`, Happier intentionally supports user-configured HTTP and HTTPS Anthropic-compatible endpoints through `ANTHROPIC_BASE_URL`. Invalid explicit URLs must fail without fallback to Anthropic, redirects must be rejected, and credentials must not be rerouted to a different origin.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts`:
- Line 642: In the test containing getOrCreateSessionSpy.mockImplementation,
save the spy’s existing implementation before overriding it, then restore that
implementation in the existing finally block alongside sessionSyncClientSpy.
Ensure later tests receive their configured session responses.

In
`@apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.ts`:
- Around line 59-65: Add regression tests in
claudeUnifiedResumeChoiceStartupResolver.test.ts for startupMode.ultracode ===
true, asserting the resolver accepts both the ultracode and xhigh targets. Keep
existing reasoningEffort coverage unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c82af40-881c-4a6a-a2ac-90015521f667

📥 Commits

Reviewing files that changed from the base of the PR and between 86f1b51 and 599812c.

📒 Files selected for processing (9)
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.test.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts
  • apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.ts
  • docs/agents-catalog.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts
  • docs/agents-catalog.md
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts

@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@greptile-apps please review the current head 2484abc.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

@leeroybrun I will review the current head of #237.


🧠 Learnings used
Learnt from: leeroybrun
Repo: happier-dev/happier PR: 237
File: apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts:77-95
Timestamp: 2026-08-11T23:39:07.782Z
Learning: For Claude model discovery in `apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts`, Happier intentionally supports user-configured HTTP and HTTPS Anthropic-compatible endpoints through `ANTHROPIC_BASE_URL`. Invalid explicit URLs must fail without fallback to Anthropic, redirects must be rejected, and credentials must not be rerouted to a different origin.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.test.ts (1)

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

Add explicit TypeScript types.

Declare the _target and capture parameter types and the Promise<void> return types.

As per coding guidelines: “Always use types for function parameters and return values in TypeScript.”

Suggested annotations
-        wait: async () => undefined,
+        wait: async (): Promise<void> => undefined,
...
-    async (_target, capture) => {
+    async (_target: 'ultracode' | 'xhigh', capture: string): Promise<void> => {

Also applies to: 423-423

🤖 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
`@apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.test.ts`
at line 415, Update the async callback functions around the affected resolver
tests to explicitly type both parameters, _target and capture, and annotate
their return type as Promise<void>. Apply the same annotations to the additional
callback occurrence noted in the review, using the existing target and capture
types from the surrounding test APIs.

Source: Coding guidelines

🤖 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
`@apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.test.ts`:
- Around line 435-436: Extend the assertions for the relevant resume-choice test
to verify that createFakeControlPort’s sentRaw collection is empty, alongside
the existing sentLiteral and sentKeys checks, ensuring no raw control sequence
bypasses the test.

---

Nitpick comments:
In
`@apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.test.ts`:
- Line 415: Update the async callback functions around the affected resolver
tests to explicitly type both parameters, _target and capture, and annotate
their return type as Promise<void>. Apply the same annotations to the additional
callback occurrence noted in the review, using the existing target and capture
types from the surrounding test APIs.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d813c218-261c-4c34-b349-a03379b6cf0b

📥 Commits

Reviewing files that changed from the base of the PR and between 599812c and 2484abc.

📒 Files selected for processing (2)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/unifiedTerminal/resumeChoice/claudeUnifiedResumeChoiceStartupResolver.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts

@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@leeroybrun

Copy link
Copy Markdown
Collaborator

@greptileai review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026
@leeroybrun

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@greptileai review the current head c4ef502.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

@leeroybrun I will review the current head of #237, including commit c4ef502ae1fd89993d2fa0de1e4c2bb936fca800.

I will apply the existing endpoint-routing guidance for Claude model discovery during this review.


🧠 Learnings used
Learnt from: leeroybrun
Repo: happier-dev/happier PR: 237
File: apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts:77-95
Timestamp: 2026-08-11T23:39:07.782Z
Learning: For Claude model discovery in `apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts`, Happier intentionally supports user-configured HTTP and HTTPS Anthropic-compatible endpoints through `ANTHROPIC_BASE_URL`. Invalid explicit URLs must fail without fallback to Anthropic, redirects must be rejected, and credentials must not be rerouted to a different origin.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts`:
- Around line 640-684: Save the existing implementation of getOrCreateSessionSpy
before replacing it with the fast-start test implementation, then restore that
saved implementation in the finally cleanup block. Keep the existing runtime
capability and module mock cleanup unchanged.

In `@apps/cli/src/backends/claude/runClaude.ts`:
- Around line 1237-1249: Update the standard initial mode construction around
resolveClaudeInstalledRuntimeSessionMode to retain the mode object, then refresh
model effort data in onSessionReady before updating that object’s model and
effort-tier fields, matching the fast-start flow. Ensure supported selected
models receive effort and Ultracode values on the initial standard launch, and
add a regression test covering standard-runner startup.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: be7930be-5f78-4a5c-98dd-e553446a1854

📥 Commits

Reviewing files that changed from the base of the PR and between 4641956 and c4ef502.

📒 Files selected for processing (20)
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.test.ts
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.ts
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts
  • apps/cli/src/backends/claude/runClaude.ts
  • apps/cli/src/backends/claude/sessionControls/probeClaudeHelpText.ts
  • apps/cli/src/backends/claude/sessionControls/probeClaudeInstalledRuntimeCapabilities.test.ts
  • apps/cli/src/backends/claude/sessionControls/probeClaudeInstalledRuntimeCapabilities.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.test.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.test.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.staticOnly.test.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.ts
  • docs/agents-catalog.md
  • packages/agents/src/models.ts
💤 Files with no reviewable changes (1)
  • apps/cli/src/backends/claude/sessionControls/probeClaudeHelpText.ts
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.test.ts
  • apps/cli/src/backends/claude/sessionControls/resolveClaudeSessionModelsState.test.ts
  • docs/agents-catalog.md
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.ts
  • apps/cli/src/backends/claude/preflight/claudePreflightModelsProbeAdapter.test.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.ts
  • apps/cli/src/backends/claude/models/fetchAnthropicModels.ts
  • packages/agents/src/models.ts
  • apps/cli/src/backends/claude/sessionControls/publishClaudeSessionModelsMetadataBestEffort.test.ts
  • apps/cli/src/backends/claude/models/resolveClaudeModelCatalog.ts
  • apps/cli/src/capabilities/probes/agentModelsProbe.ts

Comment thread apps/cli/src/backends/claude/runClaude.ts Outdated
@leeroybrun

Copy link
Copy Markdown
Collaborator

Both exact-head CodeRabbit findings were reproduced and fixed in commit 139c0ee. The standard and fast paths now share one startup effort-evidence refresh owner, and the leaked test mock is restored. @coderabbitai please review the current exact head 139c0ee. @greptile-apps please review the current exact head 139c0ee, including all changes since your prior review.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='139c0eeecd5b668e5e964d75e2c1adeef1c9ab22'

echo '== Checked-out commit =='
git rev-parse HEAD
echo '== Target commit =='
git rev-parse "${target}^{commit}"

echo '== Commit metadata =='
git show --no-ext-diff --format=fuller --no-renames --stat "$target"

echo '== Changed files =='
git diff-tree --no-commit-id --name-status -r "$target"

echo '== Patch =='
git diff --no-ext-diff --no-renames "${target}^" "$target" -- \
  apps/cli/src/backends/claude/runClaude.ts \
  apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts \
  apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts

echo '== Outline: production startup file =='
ast-grep outline apps/cli/src/backends/claude/runClaude.ts --items all --type function --match 'runClaude|refresh|Tracker|fastStart|startup' || true

echo '== Refresh tracker call sites =='
rg -n -C 5 --type ts \
  '\b(createClaudeModelEffortLevelsTracker|modelEffortTracker\.(refresh|refreshWithin)|refresh.*[Ee]ffort)' \
  apps/cli/src/backends/claude

echo '== Test mock setup and restoration =='
rg -n -C 8 --type ts \
  '\b(vi\.(mock|unmock|restoreAllMocks|resetAllMocks|clearAllMocks)|mock.*(restore|reset)|afterEach|beforeEach)\b' \
  apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts \
  apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts

Length of output: 81277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== runClaude: initial mode construction and standard loop call =='
sed -n '1200,1335p' apps/cli/src/backends/claude/runClaude.ts

echo '== runClaude: fast-start initial mode construction and loop call =='
sed -n '2020,2165p' apps/cli/src/backends/claude/runClaude.ts

echo '== loop outline =='
ast-grep outline apps/cli/src/backends/claude/loop.ts --items all --type function --match 'loop|onSessionReady|initialClaudeUnifiedTerminalMode'

echo '== loop: initial mode and session-ready call sites =='
rg -n -C 12 --type ts \
  '\b(initialClaudeUnifiedTerminalMode|onSessionReady)\b' \
  apps/cli/src/backends/claude/loop.ts

echo '== mode consumers in Claude backend =='
rg -n -C 5 --type ts \
  '\binitialClaudeUnifiedTerminalMode\b' \
  apps/cli/src/backends/claude

echo '== Changed standard-path test and test lifecycle =='
sed -n '280,325p' apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts
sed -n '635,735p' apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts

echo '== Changed fast-start test and surrounding lifecycle =='
sed -n '375,470p' apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
sed -n '605,700p' apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts

Length of output: 53852


@leeroybrun I reviewed commit 139c0eeecd5b668e5e964d75e2c1adeef1c9ab22.

I found no new issues in the reported fixes.

loop awaits onSessionReady before it passes initialClaudeUnifiedTerminalMode to either Claude runtime launcher. Both startup paths now refresh and apply the model-specific effort evidence through the same helper. The fast-start test restores the prior getOrCreateSessionSpy implementation in its cleanup path.

I did not run tests in the sandbox.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts (1)

654-693: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Use typed loop parameters and test the real loop path.

This test mocks the first-party loop orchestration helper, so it does not verify that initialClaudeUnifiedTerminalMode reaches the launcher. Use a real loop with only the process or transport boundary mocked. Replace both any annotations with Parameters<typeof loop>[0] and EnhancedMode | undefined.

🤖 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 `@apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts`
around lines 654 - 693, Update the startup metadata ordering test to exercise
the real Claude loop orchestration, mocking only the process or transport
boundary needed to stop execution rather than mocking loop itself. Replace the
loop callback parameter’s any type with Parameters<typeof loop>[0], and type
initialMode as EnhancedMode | undefined while asserting the mode reaches the
launcher.

Source: Coding guidelines

🤖 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 `@apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts`:
- Around line 688-690: Replace the broad any annotations in the loop mock setup
with a LoopParams type derived from Parameters<typeof loop>[0]. Use LoopParams
for the mock callback parameter and type initialMode consistently with
params.initialClaudeUnifiedTerminalMode.

---

Nitpick comments:
In `@apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts`:
- Around line 654-693: Update the startup metadata ordering test to exercise the
real Claude loop orchestration, mocking only the process or transport boundary
needed to stop execution rather than mocking loop itself. Replace the loop
callback parameter’s any type with Parameters<typeof loop>[0], and type
initialMode as EnhancedMode | undefined while asserting the mode reaches the
launcher.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 817bdfe1-881a-4ab4-8291-3b2c3c7931a1

📥 Commits

Reviewing files that changed from the base of the PR and between c4ef502 and 139c0ee.

📒 Files selected for processing (3)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts
  • apps/cli/src/backends/claude/runClaude.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/cli/src/backends/claude/runClaude.fastStart.integration.test.ts
  • apps/cli/src/backends/claude/runClaude.ts

Comment thread apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts Outdated
@leeroybrun

Copy link
Copy Markdown
Collaborator

Addressed the latest exact-head CodeRabbit type-safety finding in fa239ff4d34213a613bdf515dd961195106f742c7. @coderabbitai please review the current exact head fa239ff4d34213a613bdf515dd961195106f742c7. @greptile-apps please review the current exact head fa239ff4d34213a613bdf515dd961195106f742c7.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='fa239ff4d34213a613bdf515dd961195106f742c7'

echo '== Checked-out commit =='
git rev-parse HEAD || true
echo '== Target commit =='
git rev-parse "${target}^{commit}" 2>&1 || true

echo '== Commit metadata =='
git show --no-ext-diff --format=fuller --no-renames --stat "$target" 2>&1 | head -100

echo '== Changed files =='
git diff-tree --no-commit-id --name-status -r "$target" 2>&1

echo '== Patch =='
git diff --no-ext-diff --no-renames "${target}^" "$target" 2>&1

Length of output: 1427


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Checked-out commit (actual current head) =='
git rev-parse HEAD

echo '== Commit metadata =='
git show --no-ext-diff --format=fuller --no-renames --stat HEAD | head -100

echo '== Changed files vs parent =='
git diff-tree --no-commit-id --name-status -r HEAD

echo '== Full patch vs parent =='
git diff --no-ext-diff --no-renames HEAD~1 HEAD

Length of output: 2297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Usage of initialMode after this block =='
rg -n -B2 -A15 'let initialMode' apps/cli/src/backends/claude/runClaude.startupMetadataOrdering.test.ts

echo '== loop() signature for initialClaudeUnifiedTerminalMode type =='
ast-grep outline apps/cli/src/backends/claude/loop.ts --items all --type function --match 'loop'
rg -n 'initialClaudeUnifiedTerminalMode' apps/cli/src/backends/claude/loop.ts | head -20

Length of output: 1866


The fix is confirmed. LoopParams['initialClaudeUnifiedTerminalMode'] correctly derives the type EnhancedMode | undefined from LoopOptions, replacing the two any casts. The mock and its later usage stay consistent with loop's real signature, so this closes the type-safety finding without any regression.

@leeroybrun I reviewed commit fa239ff (the current head; the hash you gave has one extra character, so I used the actual HEAD). This is the only change since the prior reviewed commit.

The fix replaces two any annotations in runClaude.startupMetadataOrdering.test.ts with LoopParams['initialClaudeUnifiedTerminalMode'], derived from Parameters<typeof loop>[0]. This type matches loop's real initialClaudeUnifiedTerminalMode parameter and preserves the existing mock behavior and later usage. I found no new issues.

I did not run tests in the sandbox.


🧠 Learnings used
Learnt from: leeroybrun
Repo: happier-dev/happier PR: 237
File: apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts:77-95
Timestamp: 2026-08-11T23:39:07.782Z
Learning: For Claude model discovery in `apps/cli/src/backends/claude/models/resolveClaudeModelProbeTarget.ts`, Happier intentionally supports user-configured HTTP and HTTPS Anthropic-compatible endpoints through `ANTHROPIC_BASE_URL`. Invalid explicit URLs must fail without fallback to Anthropic, redirects must be rejected, and credentials must not be rerouted to a different origin.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@leeroybrun

Copy link
Copy Markdown
Collaborator

Follow-up implementation and verification

Posted by @happier-bot on behalf of the implementation/review pass.

We completed the requested follow-up work on this PR and pushed it to the current head, fa239ff4d70ba278822d065a1021a372cb511fe1.

The main product decision is now represented explicitly: when an authenticated Claude Models API request succeeds, that response owns model membership and API-provided capability/context facts. Happier's curated static catalog enriches matching returned rows with presentation and known local metadata, but it no longer silently adds model IDs that the selected account did not return. A successful empty result remains authoritative empty. Static rows remain the cold fallback when discovery fails, while a bounded same-credential last-good dynamic snapshot protects an already-running daemon from transient failures.

The follow-up commits also:

  • preserve older/unusual model IDs returned by the selected account instead of applying a locally curated generation cutoff;
  • keep API facts authoritative while retaining curated presentation and advanced option metadata for exact matching models;
  • separate generic --effort support from actual installed-runtime Ultracode recognition and fail closed when either capability is unavailable;
  • retain discovered model-scoped effort evidence through startup so the first ordinary or fast-start message uses the correct effort/Ultracode admission decision;
  • preserve authoritative-empty semantics through the generic provider-owned probe boundary;
  • retain the exact selected connected-service credential identity in probing and cache partitioning instead of falling back to the daemon's ambient Claude home;
  • keep endpoint eligibility permissive: an explicitly configured compatible endpoint is not rejected merely because it is not an Anthropic hostname;
  • consolidate installed-runtime capability probing rather than leaving parallel help-text interpretations.

Why this shape: dynamic discovery should answer “what this selected account can use now,” while static knowledge remains valuable for display metadata, known aliases/options, and failure continuity. Treating the static and dynamic lists as an unconditional union would continue exposing unavailable models; deleting the static catalog entirely would make transient API failures unnecessarily disruptive and would discard curated option metadata that the endpoint does not fully describe.

We also manually ported the same intent into the evolved dev architecture rather than copying the Remote-Dev implementation. Dev already has generic Provider catalog, purpose-binding, request-auth, scheduler, capability, and UI-cache owners, so the port extends those owners and does not add a Claude-local token parser, HTTP client, or competing cache. That scoped port is committed separately as b059a7c2cbaac9fb413e217c4856be34953645ad, with Co-authored-by: @danljungstrom.

Verification

  • CodeRabbit approved the exact current PR head.
  • Greptile completed successfully on the exact current PR head.
  • GitHub currently reports the PR as MERGEABLE with reviewDecision: APPROVED.
  • Focused Remote-Dev catalog, startup ordering, fast-start, installed-runtime capability, preflight, and provider-owned empty-result regressions were added/updated during the follow-up.
  • The Dev intent-port passed the focused Protocol catalog suite (25/25), Protocol build, model-override contract (8/8), capability handler (10/10), capability service (2/2), scheduler/observation (18/18), and Claude manifest/runtime checks (35/35).
  • An independent Dev finding-delta review found no remaining material OAuth-catalog correctness, security, or split-brain issue; it specifically rechecked cancellation/plugin-generation currentness and cache rekeying after a revision-changing 401 refresh.

Remaining evidence gaps

The repository-wide PR check set is not globally green: several broad typecheck, package, E2E, Windows installer, and release-contract jobs failed or were cancelled. We are not treating those jobs as feature passes merely because the focused corridor is green. A final composed live OAuth Models API call was also not rerun because no valid live credential was available at closeout, and the Dev focused UI runner stalled and was interrupted. Those are remaining integration/release-validation gaps rather than hidden claims of completion.

Full audit and intent-port evidence is recorded in .project/reviews/2026-08-11-21-31-46-pr-237-runtime-models-b84a4c/REVIEW.md in the working repositories.

@happier-dev happier-dev deleted a comment from happier-bot Aug 12, 2026
@leeroybrun

Copy link
Copy Markdown
Collaborator

Very good, thank you very much @danljungstrom !

@leeroybrun
leeroybrun merged commit 89d49bd into happier-dev:dev Aug 12, 2026
23 of 37 checks passed
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.

2 participants