fix(docker): report source build version (#1862)

## Description

Closes #1858

Docker/Compose source builds could report stale or misleading version
information: the dashboard initially rendered a hardcoded `v0.3.0`, then
`/health` replaced it with installed package metadata, which can be
stale when building locally from `main` without release metadata in the
image.

This change makes source Docker Compose builds report an explicit
source-build identity, removes the stale dashboard fallback, and keeps
CLI/doctor version checks from treating source-build labels as
release-version drift.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Add `HEADROOM_VERSION` / `HEADROOM_BUILD_VERSION` runtime version
overrides and optional packaged `_build_info.py` metadata.
- Teach Docker Compose source builds to pass a `source-build` sentinel
that the Dockerfile expands to `source-build+g<sha>` when git metadata
is available, or `source-build+sha256.<digest>` otherwise.
- Keep release/published image builds on normal package metadata when
`HEADROOM_BUILD_VERSION` is unset.
- Include only minimal `.git` metadata in the Docker build context so
the source-build label can identify the checkout without copying git
objects.
- Treat source-build labels and raw hashes as non-release labels in
`wrap` and `doctor`, avoiding false stale-proxy restarts and drift
warnings.
- Replace the dashboard hardcoded `0.3.0` fallback with `loading` /
`unknown` and format non-release build labels without a `v` prefix.
- Include the runtime version in proxy startup logs, `/health`,
`/livez`, and OTEL service version reporting.

## Testing

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

### Test Output

```text
GitHub CI: all checks passing
- CI: build, build-wheel, lint, test shards, test-extras, test-agno, test-dashboard-ui
- Docker: docker-native-e2e, docker-wrap-e2e, docker-init-e2e
- Native wrappers: macOS, Windows, Ubuntu
- Security: CodeQL, gitleaks, pip-audit
- Governance: template, label, merge-conflicts, commitlint

$ HEADROOM_REQUIRE_RUST_CORE=false PYTHONPATH=/Users/vinaygupta/Desktop/git/headroom-fix-1858-version-mismatch pytest tests/test_package_init_lazy.py::test_version_prefers_explicit_build_env tests/test_package_init_lazy.py::test_version_label_helpers_only_prefix_release_versions tests/test_package_init_lazy.py::test_version_uses_packaged_build_metadata tests/test_package_init_lazy.py::test_observability_version_uses_runtime_version tests/test_docker_compose_persistence.py tests/test_cli_doctor.py::TestProxyLiveness::test_up_leaves_source_label_unprefixed tests/test_cli_doctor.py::TestVersionDrift::test_non_release_version_labels_skip_drift_comparison tests/test_cli/test_wrap_persistent.py::test_proxy_version_restart_ignores_non_release_source_labels tests/test_proxy_dashboard_stats_cache.py::test_dashboard_uses_cached_stats_and_lazy_history_feed_polling -q
13 passed, 1 warning

$ uvx ruff==0.15.17 check .
All checks passed!

$ uvx ruff==0.15.17 format --check .
1058 files already formatted

$ uvx mypy==1.20.2 headroom --ignore-missing-imports
Success: no issues found in 407 source files

$ git diff --check
# no output

$ docker compose config
# resolved headroom-proxy build args include HEADROOM_BUILD_VERSION: source-build

$ HEADROOM_BUILD_VERSION=6266a1d docker compose config
# explicit override is preserved as HEADROOM_BUILD_VERSION: 6266a1d

$ docker build --check --build-arg HEADROOM_BUILD_VERSION=source-build .
Check complete, no warnings found.
```

## Real Behavior Proof

- Environment: macOS local checkout, Python 3.13.5, Docker Desktop
builder `desktop-linux`, plus GitHub Actions CI.
- Exact command / steps: `docker compose config`,
`HEADROOM_BUILD_VERSION=6266a1d docker compose config`, and `docker
build --check --build-arg HEADROOM_BUILD_VERSION=source-build .`.
- Observed result: Compose defaults the top-level `headroom-proxy` build
arg to the `source-build` sentinel, preserves explicit overrides, and
Dockerfile syntax/check validation passes for the source-build path.
- Not tested: Full end-to-end release publishing flow; this PR only
changes local/source-build reporting.
- CI proof: GitHub Actions completed successfully across Docker E2E, CI
test shards, lint/type checks, native wrapper checks, security checks,
and PR governance.

## 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/CI with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

N/A

## Additional Notes

Docs and changelog are N/A for this runtime-reporting bug fix. The PR is
open and ready for review with all GitHub checks passing.
This commit is contained in:
Vinay Gupta 2026-07-08 13:32:04 -05:00 committed by GitHub
parent 5af5e22862
commit 38074888ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 316 additions and 22 deletions

View file

@ -1,5 +1,9 @@
# VCS
.git
.git/*
!.git/HEAD
!.git/packed-refs
!.git/refs/
!.git/refs/**
.github
.github/*
!.github/plugin/

View file

@ -7,6 +7,8 @@ ARG PYTHON_SITE_PACKAGES=/usr/local/lib/python${PYTHON_VERSION}/site-packages
FROM python:${PYTHON_VERSION}-slim AS builder
ARG UV_VERSION
ARG PYTHON_SITE_PACKAGES
ARG HEADROOM_BUILD_VERSION=""
# build-essential / g++ for any C extension wheels uv may need to build
# from source. curl + ca-certificates are required by the rustup
@ -51,6 +53,85 @@ RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=cache,target=/build/target \
uv pip install --system ".[${HEADROOM_EXTRAS}]"
RUN --mount=type=bind,source=.,target=/context,readonly \
HEADROOM_BUILD_VERSION="${HEADROOM_BUILD_VERSION}" PYTHON_SITE_PACKAGES="${PYTHON_SITE_PACKAGES}" python - <<'PY'
import hashlib
import os
from pathlib import Path
def git_revision(context: Path) -> str | None:
git_dir = context / ".git"
head_path = git_dir / "HEAD"
if not head_path.exists():
return None
head = head_path.read_text(encoding="utf-8").strip()
if head.startswith("ref: "):
ref_name = head.removeprefix("ref: ").strip()
ref_path = git_dir / ref_name
if ref_path.exists():
head = ref_path.read_text(encoding="utf-8").strip()
else:
packed_refs = git_dir / "packed-refs"
if not packed_refs.exists():
return None
for line in packed_refs.read_text(encoding="utf-8").splitlines():
if line.startswith("#") or not line.strip():
continue
sha, _, name = line.partition(" ")
if name.strip() == ref_name:
head = sha
break
else:
return None
return head[:12] if len(head) >= 7 and all(c in "0123456789abcdef" for c in head.lower()) else None
def source_digest(root: Path) -> str:
digest = hashlib.sha256()
inputs = (
"pyproject.toml",
"uv.lock",
"README.md",
"Cargo.toml",
"Cargo.lock",
"rust-toolchain.toml",
"crates",
"headroom",
)
for name in inputs:
path = root / name
if not path.exists():
continue
files = [path] if path.is_file() else sorted(p for p in path.rglob("*") if p.is_file())
for file in files:
digest.update(file.relative_to(root).as_posix().encode("utf-8"))
digest.update(b"\0")
digest.update(file.read_bytes())
digest.update(b"\0")
return digest.hexdigest()[:12]
build_version = os.environ["HEADROOM_BUILD_VERSION"].strip()
if not build_version:
print("no Headroom build version override provided; using installed package metadata")
raise SystemExit(0)
if build_version == "source-build":
revision = git_revision(Path("/context"))
build_version = (
f"source-build+g{revision}"
if revision
else f"source-build+sha256.{source_digest(Path('/build'))}"
)
package_dir = Path(os.environ["PYTHON_SITE_PACKAGES"]) / "headroom"
(package_dir / "_build_info.py").write_text(
"BUILD_VERSION = " + repr(build_version) + "\n",
encoding="utf-8",
)
print("baked Headroom build version: " + build_version)
PY
# Build-stage smoke check: verify the extension loads end-to-end inside
# the build image before we copy site-packages into the runtime image.
# If this fails, the runtime image would fail Phase A0's fail-loud
@ -145,4 +226,4 @@ ENTRYPOINT ["python3", "-m", "headroom.cli", "proxy"]
CMD ["--host", "0.0.0.0", "--port", "8787"]
# Default published image remains python-slim runtime
FROM runtime-slim-base AS runtime
FROM runtime-slim-base AS runtime

View file

@ -1,6 +1,9 @@
services:
headroom-proxy:
build: .
build:
context: .
args:
HEADROOM_BUILD_VERSION: ${HEADROOM_BUILD_VERSION:-source-build}
command: ["--host", "0.0.0.0"]
environment:
- HEADROOM_HOST=0.0.0.0

View file

@ -2,10 +2,63 @@
from __future__ import annotations
import importlib
import os
import re
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
UNKNOWN_VERSION = "unknown"
VERSION_ENV_VARS = ("HEADROOM_VERSION", "HEADROOM_BUILD_VERSION")
RELEASE_VERSION_RE = re.compile(r"^v?\d+\.\d+\.\d+$")
def _clean_version(value: object) -> str | None:
"""Return a non-empty version string, if one is present."""
if not isinstance(value, str):
return None
stripped = value.strip()
return stripped or None
def is_release_version(value: object) -> bool:
"""Return whether a value is a comparable release version."""
cleaned = _clean_version(value)
return bool(cleaned and RELEASE_VERSION_RE.fullmatch(cleaned))
def normalize_release_version(value: object) -> str | None:
"""Return a comparable release version without a display prefix."""
cleaned = _clean_version(value)
if cleaned is None or RELEASE_VERSION_RE.fullmatch(cleaned) is None:
return None
return cleaned[1:] if cleaned.startswith("v") else cleaned
def format_version_label(value: object) -> str:
"""Return a user-facing version label without prefixing source labels."""
cleaned = _clean_version(value) or UNKNOWN_VERSION
if is_release_version(cleaned) and not cleaned.startswith("v"):
return f"v{cleaned}"
return cleaned
def _env_version() -> str | None:
"""Return an explicit runtime/build version override."""
for name in VERSION_ENV_VARS:
value = _clean_version(os.environ.get(name))
if value:
return value
return None
def _packaged_build_version() -> str | None:
"""Return Docker/image build metadata baked into the installed package."""
try:
build_info = importlib.import_module("headroom._build_info")
except ModuleNotFoundError:
return None
return _clean_version(getattr(build_info, "BUILD_VERSION", None))
def _source_root() -> Path | None:
@ -46,12 +99,20 @@ def _source_tree_version(root: Path) -> str | None:
def get_version() -> str:
"""Return Headroom's runtime version."""
env_version = _env_version()
if env_version:
return env_version
root = _source_root()
if root is not None:
source_version = _source_tree_version(root)
if source_version:
return source_version
build_version = _packaged_build_version()
if build_version:
return build_version
try:
return version("headroom-ai")
except PackageNotFoundError:

View file

@ -22,6 +22,7 @@ from typing import Any
import click
from headroom._version import format_version_label, normalize_release_version
from headroom.install.health import probe_json
from headroom.install.paths import claude_settings_path, codex_config_path
from headroom.install.state import list_manifests
@ -97,7 +98,7 @@ def check_proxy_liveness(livez: dict[str, Any] | None, base_url: str) -> CheckRe
return CheckResult(
name="proxy",
status=PASS,
summary=f"running at {base_url} ({uptime_text}, v{version})",
summary=f"running at {base_url} ({uptime_text}, {format_version_label(version)})",
)
@ -112,14 +113,26 @@ def check_version_drift(livez: dict[str, Any] | None, installed: str) -> CheckRe
status=WARN,
summary=f"cannot compare versions (proxy {running}, installed {installed})",
)
if running != installed:
running_release = normalize_release_version(running)
installed_release = normalize_release_version(installed)
if running_release is None or installed_release is None:
return CheckResult(
name="version",
status=SKIP,
summary=f"source/non-release version label (proxy {running}, installed {installed})",
)
if running_release != installed_release:
return CheckResult(
name="version",
status=WARN,
summary=f"version drift: proxy {running}, installed {installed}",
hint="restart the proxy to pick up new code: headroom proxy",
)
return CheckResult(name="version", status=PASS, summary=f"proxy matches installed v{installed}")
return CheckResult(
name="version",
status=PASS,
summary=f"proxy matches installed {format_version_label(installed)}",
)
def check_claude_routing(settings_path: Path, port: int) -> CheckResult:
@ -398,7 +411,9 @@ def _render(checks: list[CheckResult], port: int, installed: str) -> None:
from rich.table import Table
console = Console()
console.print(f"[bold]Headroom Doctor[/bold] [dim]v{installed} · port {port}[/dim]\n")
console.print(
f"[bold]Headroom Doctor[/bold] [dim]{format_version_label(installed)} · port {port}[/dim]\n"
)
table = Table(show_header=True, header_style="bold")
table.add_column("check")
table.add_column("status")

View file

@ -45,6 +45,7 @@ import click
from headroom import fsutil
from headroom._version import __version__ as _HEADROOM_VERSION
from headroom._version import normalize_release_version as _normalize_release_version
from headroom.agent_savings import (
apply_agent_savings_env_defaults,
)
@ -2531,11 +2532,12 @@ def _proxy_version(payload: dict[str, Any] | None) -> str | None:
def _proxy_needs_version_restart(payload: dict[str, Any] | None) -> bool:
"""Return True when a running Headroom proxy uses a different package version."""
running_version = _proxy_version(payload)
running_release = _normalize_release_version(running_version)
current_release = _normalize_release_version(_HEADROOM_VERSION)
return (
running_version is not None
and running_version != "unknown"
and _HEADROOM_VERSION != "unknown"
and running_version != _HEADROOM_VERSION
running_release is not None
and current_release is not None
and running_release != current_release
)

View file

@ -109,7 +109,7 @@
<header class="border-b border-border px-6 py-4 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
<div class="flex items-center gap-4">
<h1 class="text-xl font-semibold tracking-tight">HEADROOM</h1>
<span class="text-xs text-gray-500 font-mono" x-text="'v' + version"></span>
<span class="text-xs text-gray-500 font-mono" x-text="formatVersion(version)"></span>
</div>
<div class="flex flex-col gap-3 md:flex-row md:items-center md:gap-6">
<div class="inline-flex rounded-lg border border-border bg-surface p-1">
@ -1795,7 +1795,7 @@
stats: {},
historyStats: {},
healthy: true,
version: '0.3.0',
version: 'loading',
lastUpdate: 'never',
viewMode: 'session',
historyGranularity: 'daily',
@ -1862,6 +1862,12 @@
}
},
formatVersion(value) {
const label = String(value || 'unknown').trim();
if (label === 'loading' || label === 'unknown') return label;
return /^\d+\.\d+\.\d+$/.test(label) ? 'v' + label : label;
},
async fetchStats() {
try {
const [statsRes, healthRes] = await Promise.all([
@ -1872,7 +1878,7 @@
this.stats = await statsRes.json();
const health = await healthRes.json();
this.healthy = health.status === 'healthy';
this.version = health.version || '0.3.0';
this.version = health.version || 'unknown';
this.log_full_messages = this.stats.log_full_messages || false;
// Update history for sparklines

View file

@ -5,14 +5,14 @@ from __future__ import annotations
import logging
import os
from dataclasses import dataclass, field
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as package_version
from threading import Lock
from typing import Any, Literal
from opentelemetry import metrics
from opentelemetry.metrics import CallbackOptions, Observation
from headroom._version import get_version
logger = logging.getLogger(__name__)
MetricExporter = Literal["console", "otlp_http"]
@ -28,10 +28,7 @@ _owned_metrics_config: OTelMetricsConfig | None = None
def _headroom_version() -> str:
try:
return package_version("headroom-ai")
except PackageNotFoundError:
return "unknown"
return get_version()
def _parse_bool(raw: str | None, default: bool = False) -> bool:

View file

@ -1405,7 +1405,7 @@ class HeadroomProxy(
self.http_client_h1 = (
self.http_client if not _http2 else httpx.AsyncClient(http2=False, **_client_kwargs)
)
logger.info("Headroom Proxy started")
logger.info("Headroom Proxy started (version %s)", __version__)
logger.info(f"Optimization: {'ENABLED' if self.config.optimize else 'DISABLED'}")
self.config.mode = normalize_proxy_mode(self.config.mode)
logger.info(f"Mode: {self.config.mode}")

View file

@ -290,6 +290,22 @@ def test_ensure_proxy_restarts_idle_stale_ephemeral_proxy(monkeypatch) -> None:
assert calls[1][0] == "start"
def test_proxy_version_restart_ignores_non_release_source_labels(monkeypatch) -> None:
monkeypatch.setattr(wrap_cli, "_HEADROOM_VERSION", "0.29.0")
assert wrap_cli._proxy_needs_version_restart({"version": "source-build+g6266a1d774b5"}) is False
assert (
wrap_cli._proxy_needs_version_restart({"version": "source-build+sha.abcdef123456"}) is False
)
assert wrap_cli._proxy_needs_version_restart({"version": "6266a1d"}) is False
assert wrap_cli._proxy_needs_version_restart({"version": "0.29.0+gabcdef0"}) is False
monkeypatch.setattr(wrap_cli, "_HEADROOM_VERSION", "source-build+sha.abcdef123456")
assert wrap_cli._proxy_needs_version_restart({"version": "0.29.0"}) is False
monkeypatch.setattr(wrap_cli, "_HEADROOM_VERSION", "0.29.1")
assert wrap_cli._proxy_needs_version_restart({"version": "0.29.0"}) is True
def test_ensure_proxy_restarts_ephemeral_proxy_for_openai_api_url_mismatch(monkeypatch) -> None:
calls: list[object] = []
health = {

View file

@ -56,6 +56,13 @@ class TestProxyLiveness:
assert "v0.26.0" in result.summary
assert "3d" in result.summary
def test_up_leaves_source_label_unprefixed(self):
livez = {**LIVEZ_OK, "version": "source-build+sha.abcdef123456"}
result = check_proxy_liveness(livez, "http://127.0.0.1:8787")
assert result.status == PASS
assert "source-build+sha.abcdef123456" in result.summary
assert "vsource-build" not in result.summary
class TestVersionDrift:
def test_match_passes(self):
@ -74,6 +81,21 @@ class TestVersionDrift:
assert check_version_drift({"version": "unknown"}, "0.26.0").status == WARN
assert check_version_drift(LIVEZ_OK, "unknown").status == WARN
@pytest.mark.parametrize(
("running", "installed"),
[
("source-build+g6266a1d774b5", "0.26.0"),
("source-build+sha.abcdef123456", "0.26.0"),
("6266a1d", "0.26.0"),
("0.26.0+gabcdef0", "0.26.0"),
("0.26.0", "source-build+sha.abcdef123456"),
],
)
def test_non_release_version_labels_skip_drift_comparison(self, running, installed):
result = check_version_drift({"version": running}, installed)
assert result.status == SKIP
assert "drift" not in result.summary
class TestClaudeRouting:
def test_missing_file_warns(self, tmp_path):

View file

@ -14,3 +14,21 @@ def test_top_level_compose_pins_headroom_state_to_named_volume() -> None:
assert "- HOME=/home/nonroot" in compose
assert "- HEADROOM_WORKSPACE_DIR=/home/nonroot/.headroom" in compose
assert "- HEADROOM_CONFIG_DIR=/home/nonroot/.headroom/config" in compose
def test_top_level_compose_marks_source_build_version() -> None:
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8")
assert "HEADROOM_BUILD_VERSION: ${HEADROOM_BUILD_VERSION:-source-build}" in compose
assert 'ARG HEADROOM_BUILD_VERSION=""' in dockerfile
assert "ARG PYTHON_SITE_PACKAGES" in dockerfile
assert "if not build_version:" in dockerfile
assert "source-build+g{revision}" in dockerfile
assert "source-build+sha256." in dockerfile
assert "_build_info.py" in dockerfile
assert "import headroom._version" not in dockerfile
assert ".git/*" in dockerignore
assert "!.git/HEAD" in dockerignore
assert "!.git/refs/**" in dockerignore

View file

@ -7,6 +7,7 @@ import os
import subprocess
import sys
import textwrap
import types
from importlib.metadata import PackageNotFoundError
from pathlib import Path
from unittest.mock import patch
@ -64,6 +65,66 @@ def test_version_reports_unknown_when_distribution_metadata_is_missing() -> None
assert version_module.get_version() == version_module.UNKNOWN_VERSION
def test_version_prefers_explicit_build_env(monkeypatch) -> None:
monkeypatch.setenv("HEADROOM_BUILD_VERSION", "source-build")
with patch.object(version_module, "version", return_value="9.8.7") as package_version:
assert version_module.get_version() == "source-build"
package_version.assert_not_called()
def test_version_label_helpers_only_prefix_release_versions() -> None:
assert version_module.is_release_version("0.29.0") is True
assert version_module.is_release_version("v0.29.0") is True
assert version_module.normalize_release_version("v0.29.0") == "0.29.0"
assert version_module.is_release_version("source-build+g6266a1d774b5") is False
assert version_module.is_release_version("source-build+sha.abcdef123456") is False
assert version_module.is_release_version("6266a1d") is False
assert version_module.is_release_version("0.29.0+gabcdef0") is False
assert version_module.format_version_label("0.29.0") == "v0.29.0"
assert version_module.format_version_label("v0.29.0") == "v0.29.0"
assert (
version_module.format_version_label("source-build+sha.abcdef123456")
== "source-build+sha.abcdef123456"
)
assert (
version_module.format_version_label("source-build+g6266a1d774b5")
== "source-build+g6266a1d774b5"
)
assert version_module.format_version_label("6266a1d") == "6266a1d"
assert version_module.format_version_label(None) == version_module.UNKNOWN_VERSION
def test_version_uses_packaged_build_metadata(
monkeypatch,
) -> None:
build_info = types.ModuleType("headroom._build_info")
build_info.BUILD_VERSION = "0.29.0+gabcdef0"
monkeypatch.setitem(sys.modules, "headroom._build_info", build_info)
with (
patch.object(version_module, "_source_root", return_value=None),
patch.object(version_module, "version", return_value="0.29.0") as package_version,
):
assert version_module.get_version() == "0.29.0+gabcdef0"
package_version.assert_not_called()
def test_observability_version_uses_runtime_version(monkeypatch) -> None:
from headroom.observability import metrics as metrics_module
monkeypatch.setattr(
metrics_module,
"get_version",
lambda: "source-build+sha.abcdef123456",
)
assert metrics_module._headroom_version() == "source-build+sha.abcdef123456"
def test_version_prefers_source_tree_release_history() -> None:
with (
patch.object(version_module, "_source_root", return_value=Path(".")),

View file

@ -591,6 +591,12 @@ def test_dashboard_uses_cached_stats_and_lazy_history_feed_polling() -> None:
html = get_dashboard_html()
assert "fetch('/stats?cached=1')" in html
assert "version: 'loading'" in html
assert 'x-text="formatVersion(version)"' in html
assert "return /^\\d+\\.\\d+\\.\\d+$/.test(label)" in html
assert "return /^\\d/.test(value)" not in html
assert "this.version = health.version || 'unknown'" in html
assert "0.3.0" not in html
assert "@click=\"setViewMode('history')\"" in html
assert '@click="toggleFeed()"' in html
assert "this.viewMode === 'history'" in html

View file

@ -8,7 +8,7 @@ pytest.importorskip("httpx")
from fastapi.testclient import TestClient
from headroom.proxy.server import ProxyConfig, create_app
from headroom.proxy.server import ProxyConfig, __version__, create_app
@pytest.fixture
@ -37,6 +37,7 @@ def test_livez_reports_process_health(client):
assert data["service"] == "headroom-proxy"
assert data["status"] == "healthy"
assert data["alive"] is True
assert data["version"] == __version__
assert data["uptime_seconds"] >= 0
@ -73,6 +74,7 @@ def test_health_preserves_backwards_compatible_config_payload(client):
data = response.json()
assert data["status"] == "healthy"
assert data["ready"] is True
assert data["version"] == __version__
config = data["config"]
assert config["backend"] == "anthropic"
assert config["optimize"] is False