fix(doctor): flag ollama launch claude proxy bypass instead of misdirecting (#2566)

## Description

Addresses the diagnostic half of #2199.

`ollama launch claude` sets `ANTHROPIC_BASE_URL=http://127.0.0.1:11434`
in the launched Claude Code child. That process env outranks the `env`
block a persistent Headroom install writes to `~/.claude/settings.json`,
so Claude Code talks to Ollama and never reaches the proxy — 0% savings,
nothing on the dashboard, no error.

`headroom doctor`'s routing classifier made it worse: seeing a loopback
`:11434`, it reported `routed to port 11434, but doctor probed port
8787` and hinted `re-run with: headroom doctor --port 11434` — sending
the user to re-probe Ollama's endpoint as if it were their proxy.

Closes #2199

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)

## Changes Made

- `_classify_routing_url` now recognizes Ollama's fixed default port:
the check names the `ollama launch claude` bypass and points at the
proxy-chaining path instead of the red-herring `--port 11434` re-probe
hint.
- Fires for both the shell-env and settings-file routing checks that
share the classifier.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [ ] Manual testing performed

### Test Output

```text
$ pytest tests/test_cli_doctor.py -q
1 failed, 68 passed in 3.05s
# The lone failure is test_remote_control_warning_exits_1 — pre-existing and
# unrelated: it reads real ~/.headroom stats and fails on a clean tree with or
# without this change (does not exist / does not pass on main either).

$ pytest tests/test_cli_doctor.py -k ollama -q
1 passed, 68 deselected

$ ruff check headroom/cli/doctor.py tests/test_cli_doctor.py
All checks passed!

$ mypy headroom
Success: no issues found in 509 source files
```

## Real Behavior Proof

- Environment: local checkout, Python venv, `pytest`/`ruff`/`mypy` as
above.
- Exact command / steps: `tests/test_cli_doctor.py` pins the
Ollama-aware message + hint emitted by `_classify_routing_url` for a
loopback `:11434` routing URL.
- Observed result: doctor now reports the `ollama launch claude` bypass
and the proxy-chaining fix instead of `re-run with: headroom doctor
--port 11434`.
- Not tested: no live `ollama launch claude` run; verified at the
classifier boundary.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I did **not** edit `CHANGELOG.md` — it is generated by
release-please from my Conventional Commit PR title (a CI guard enforces
this)

## Additional Notes

Scope: this is only the *diagnostic* ask (#2199 part 3, requested as the
minimum). The launcher-composition and model-aware routing halves depend
on #1279's direction and are left for a maintainer steer. Documentation
item is N/A (diagnostic message change, no docs surface). The
pre-existing `test_remote_control_warning_exits_1` failure is unrelated
as noted above.
This commit is contained in:
TenderDeve
2026-08-12 10:49:44 +05:30
committed by GitHub
parent ce8ce8313f
commit 7f24d695ee
2 changed files with 34 additions and 0 deletions
+23
View File
@@ -48,6 +48,13 @@ SKIP = "skip"
_LOOPBACK_URL_RE = re.compile(r"https?://(?:127\.0\.0\.1|localhost):(\d+)")
_CODEX_BASE_URL_RE = re.compile(r'base_url\s*=\s*"https?://(?:127\.0\.0\.1|localhost):(\d+)')
# Ollama's fixed default port. `ollama launch claude` writes
# ``ANTHROPIC_BASE_URL=http://127.0.0.1:11434`` into the launched Claude Code
# child, which outranks the persistent-install env block and silently bypasses
# the Headroom proxy (issue #2199). Recognized so the routing diagnostic names
# the collision instead of telling the user to re-probe port 11434.
_OLLAMA_DEFAULT_PORT = 11434
@dataclass
class CheckResult:
@@ -344,6 +351,22 @@ def _classify_routing_url(name: str, url: str, port: int, *, source: str) -> Che
)
found_port = int(match.group(1))
if found_port != port:
if found_port == _OLLAMA_DEFAULT_PORT:
# Not a mis-probed Headroom port — this is Ollama's endpoint, so
# `headroom doctor --port 11434` would only chase a red herring.
return CheckResult(
name=name,
status=WARN,
summary=(
f"points at Ollama ({url}), not the Headroom proxy ({source}) — "
"`ollama launch claude` bypasses the persistent Headroom route"
),
hint=(
"both claim ANTHROPIC_BASE_URL; run Ollama-backed sessions "
"through Headroom by chaining the proxy at its Ollama upstream "
"(see issue #2199)"
),
)
return CheckResult(
name=name,
status=WARN,
+11
View File
@@ -390,6 +390,17 @@ class TestShellEnv:
env = {"ANTHROPIC_BASE_URL": "https://api.anthropic.com"}
assert check_shell_env(env, 8787).status == WARN
def test_ollama_launch_url_names_the_collision(self):
# `ollama launch claude` points Claude Code at Ollama's :11434, which
# outranks the persistent Headroom route (issue #2199). The diagnostic
# must name Ollama, not tell the user to re-probe port 11434.
env = {"ANTHROPIC_BASE_URL": "http://127.0.0.1:11434"}
result = check_shell_env(env, 8787)
assert result.status == WARN
assert "Ollama" in result.summary
assert "#2199" in (result.hint or "")
assert "--port 11434" not in (result.hint or "")
class TestSavings:
def test_from_stats_passes_with_totals(self, tmp_path):