feat: ship the coding profile as Headroom's out-of-box default posture (#1893)

Make a bare `headroom proxy` (and the uvicorn factory / argparse main)
default to the cache-mode coding posture instead of requiring users to
set a dozen env vars.

Profile (agent_savings.py):
* "coding" is now the DEFAULT profile (DEFAULT_PROFILE), and it is
rewritten for cache mode: proxy_mode="cache" and
compress_user_messages=True (cache mode compresses the newest
OBSERVATION delta — a user/tool turn — so compress_user must be on or
there is nothing to compress; prefix stability is preserved by the delta
engine, not by refusing to touch user turns).
* AgentSavingsProfile carries the standalone router/handler toggles too
(tool_search, cross_turn_dedup, lossless_then_lossy, protect_reads,
code_aware, effort_router, lossless, min_chars_for_block); proxy_env()
emits them. Defaults preserve current behavior for the other profiles.
* coding sets: tool_search=1, dedupe=1, lossless_then_lossy=1,
protect_reads=1, code_aware=1, effort_router=0, lossless=0,
min_chars_for_block=25. CCR stays ON (no HEADROOM_NO_CCR) so any lossy
loss is recoverable.
* apply_agent_savings_env_defaults() now honors an explicit
HEADROOM_SAVINGS_PROFILE already in the env before falling back to the
default.

Delivery (pollution-free by construction):
* MODE and savings_profile default via INLINE defaults in the config
builders (cache / coding) — no global env mutation, so unit tests that
build config directly keep clean defaults.
* The request-time toggles are seeded into os.environ (setdefault) via
seed_proxy_env_defaults() ONLY at the executable/deployment entries —
run_server() (before serving) and create_app_from_env() (uvicorn
factory) — NOT in the CLI command or any library builder, so CliRunner
tests never leak coding defaults into os.environ across tests.
* CLI code_aware now defaults ON, matching the argparse server path
(degrades to a no-op without tree-sitter).

All explicit user env vars / CLI flags still win (setdefault + `or`
fallbacks). HEADROOM_LOSSLESS was already 0 by default; unchanged.

Tests: coding-profile + CLI-proxy-env tests updated to the new defaults;
1047 passed across the touched areas (only pre-existing memory/env
failures remain).

## Description

<!-- Briefly explain the change and why it is needed. -->

Closes #

## Type of Change

- [ ] 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

- 

## Testing

<!-- Check what you actually ran, then paste the real command output
below. -->

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

### Test Output

```text
# Paste relevant command output or artifact links here
```

## Real Behavior Proof

- Environment:
- Exact command / steps:
- Observed result:
- Not tested:

## Review Readiness

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

## Checklist

- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

Add screenshots to help explain your changes.

## Additional Notes

<!-- Mention any N/A checklist items, tradeoffs, follow-ups, or
maintainer context. -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tejas Chopra 2026-07-09 10:49:54 -04:00 committed by GitHub
parent c9217856d3
commit 68676daa50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 145 additions and 22 deletions

View file

@ -4,6 +4,7 @@ from __future__ import annotations
import logging
import os
from collections.abc import MutableMapping
from dataclasses import dataclass, replace
from typing import Protocol
@ -11,6 +12,10 @@ logger = logging.getLogger(__name__)
AGENT_90_PROFILE = "agent-90"
FALLBACK_PROFILE = "balanced"
# Out-of-the-box default profile when none is requested. Headroom's primary
# workload is coding agents, and the "coding" profile encodes the cache-mode
# delta posture (see below).
DEFAULT_PROFILE = "coding"
class CompressConfigLike(Protocol):
@ -42,6 +47,17 @@ class AgentSavingsProfile:
force_kompress: bool
proxy_mode: str
accuracy_guard: str
# Standalone router/handler toggles carried through the profile so a single
# named profile seeds Headroom's full posture. Defaults below preserve the
# current global behavior, so only a profile that opts in changes anything.
tool_search: bool = False
cross_turn_dedup: bool = False
lossless_then_lossy: bool = False
protect_reads: bool = False
code_aware: bool = True
effort_router: bool = True
lossless: bool = False
min_chars_for_block: int | None = None
@property
def savings_percent(self) -> int:
@ -65,14 +81,25 @@ class AgentSavingsProfile:
),
"HEADROOM_FORCE_KOMPRESS": "1" if self.force_kompress else "0",
"HEADROOM_ACCURACY_GUARD": self.accuracy_guard,
# Standalone router/handler toggles.
"HEADROOM_TOOL_SEARCH": "1" if self.tool_search else "0",
"HEADROOM_DEDUPE": "1" if self.cross_turn_dedup else "0",
"HEADROOM_LOSSLESS_THEN_LOSSY": "1" if self.lossless_then_lossy else "0",
"HEADROOM_PROTECT_READS": "1" if self.protect_reads else "0",
"HEADROOM_CODE_AWARE_ENABLED": "1" if self.code_aware else "0",
"HEADROOM_EFFORT_ROUTER": "1" if self.effort_router else "0",
"HEADROOM_LOSSLESS": "1" if self.lossless else "0",
}
# Only pin a keep-ratio when the profile sets one; workload personas
# leave it unset so Kompress decides and the ambient default applies.
if self.target_ratio is not None:
env["HEADROOM_TARGET_RATIO"] = f"{self.target_ratio:.2f}"
# Block-compression char floor: only emit when the profile pins one.
if self.min_chars_for_block is not None:
env["HEADROOM_MIN_CHARS_FOR_BLOCK"] = str(self.min_chars_for_block)
return env
def apply_proxy_env_defaults(self, env: dict[str, str]) -> dict[str, str]:
def apply_proxy_env_defaults(self, env: MutableMapping[str, str]) -> MutableMapping[str, str]:
"""Seed proxy env defaults without overriding explicit user settings."""
for key, value in self.proxy_env().items():
@ -125,7 +152,11 @@ _PROFILES: dict[str, AgentSavingsProfile] = {
name="coding",
target_savings=0.50, # nominal (display only); savings are emergent
target_ratio=None,
compress_user_messages=False, # no prompt mutation / cache bust
# Cache mode compresses only the newest delta — a tool/user OBSERVATION —
# so compress_user must be ON or there is nothing to compress. Prefix
# stability (no bust) is preserved by the delta engine (frozen prefix +
# append-only forwarding), not by refusing to touch user turns.
compress_user_messages=True,
compress_system_messages=False, # system prompt is the hottest cache
protect_recent=2, # keep the active code working set verbatim
protect_analysis_context=True,
@ -133,8 +164,21 @@ _PROFILES: dict[str, AgentSavingsProfile] = {
max_items_after_crush=15,
smart_crusher_with_compaction=True,
force_kompress=False, # don't override diff/log lossless with lossy ML
proxy_mode="token",
proxy_mode="cache", # delta-only compression at ~0 prefix-cache busts
accuracy_guard="strict",
# Coding posture: defer non-core tool schemas, dedupe re-reads, extend
# lossy coverage when lossless found nothing, and NEVER lossy-compress a
# file read (the agent patches exact bytes). CCR stays ON (unset) so any
# lossy loss is recoverable. Effort router off; low block floor so modest
# deltas are eligible.
tool_search=True,
cross_turn_dedup=True,
lossless_then_lossy=True,
protect_reads=True,
code_aware=True,
effort_router=False,
lossless=False,
min_chars_for_block=25,
),
"general": AgentSavingsProfile(
name="general",
@ -166,7 +210,7 @@ def get_agent_savings_profile(name: str | None = None) -> AgentSavingsProfile:
to ``balanced`` rather than leaving the user with no proxy at all.
"""
key = (name or AGENT_90_PROFILE).strip().lower()
key = (name or DEFAULT_PROFILE).strip().lower()
profile = _PROFILES.get(key)
if profile is not None:
return profile
@ -181,11 +225,18 @@ def get_agent_savings_profile(name: str | None = None) -> AgentSavingsProfile:
def apply_agent_savings_env_defaults(
env: dict[str, str],
env: MutableMapping[str, str],
profile: AgentSavingsProfile | str | None = None,
) -> dict[str, str]:
"""Apply agent savings env defaults to a proxy subprocess environment."""
) -> MutableMapping[str, str]:
"""Apply agent savings env defaults to a proxy subprocess environment.
When ``profile`` is not given, an explicit ``HEADROOM_SAVINGS_PROFILE`` already
in ``env`` is honored; only when that too is absent do we fall back to the
out-of-box default (:data:`DEFAULT_PROFILE`, i.e. ``coding``).
"""
if profile is None:
profile = env.get("HEADROOM_SAVINGS_PROFILE")
resolved = (
get_agent_savings_profile(profile)
if isinstance(profile, str) or profile is None
@ -295,6 +346,24 @@ def proxy_pipeline_kwargs(config: object) -> dict[str, object]:
return kwargs
def seed_proxy_env_defaults(env: MutableMapping[str, str] | None = None) -> None:
"""Seed the process env with the savings-profile defaults (default: coding).
Call at proxy EXECUTABLE entry points (the ``headroom proxy`` command and the
uvicorn ``create_app_from_env`` factory) BEFORE building config from env. Uses
``setdefault`` semantics via :func:`apply_agent_savings_env_defaults`, so any
explicit user setting wins and the call is idempotent.
Deliberately NOT called from ``_proxy_config_from_env`` / ``ContentRouter`` or
any other library-level builder that unit tests construct directly, so those
keep clean (unseeded) defaults and test isolation is preserved.
"""
target = os.environ if env is None else env
# apply_agent_savings_env_defaults honors an explicit HEADROOM_SAVINGS_PROFILE
# already in the env and otherwise falls back to DEFAULT_PROFILE (coding).
apply_agent_savings_env_defaults(target)
def with_target_savings(
profile: AgentSavingsProfile,
target_savings: float,

View file

@ -10,7 +10,7 @@ import click
from headroom import paths as _paths
from headroom.providers.registry import resolve_api_overrides, resolve_api_targets
from headroom.proxy.modes import PROXY_MODE_TOKEN, normalize_proxy_mode
from headroom.proxy.modes import PROXY_MODE_CACHE, normalize_proxy_mode
from .main import main
@ -1033,9 +1033,10 @@ def proxy(
# Resolve anyllm provider: env var takes precedence over CLI default (matches argparse path)
effective_anyllm_provider = os.environ.get("HEADROOM_ANYLLM_PROVIDER") or anyllm_provider
# Resolve mode: CLI flag > env var > default
# Resolve mode: CLI flag > env var > default. Default is CACHE (Headroom's
# coding posture): delta-only compression at ~0 prefix-cache busts.
effective_mode: str = normalize_proxy_mode(
mode or os.environ.get("HEADROOM_MODE") or PROXY_MODE_TOKEN
mode or os.environ.get("HEADROOM_MODE") or PROXY_MODE_CACHE
)
# Stateless mode: CLI flag or env var
@ -1102,7 +1103,7 @@ def proxy(
else frozenset(),
tool_profiles=_parse_tool_profiles([]) or None,
smart_crusher_with_compaction=_get_env_bool_optional("HEADROOM_SMART_CRUSHER_COMPACTION"),
savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or None,
savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding",
target_ratio=target_ratio,
compress_system_messages=_get_env_bool_optional("HEADROOM_COMPRESS_SYSTEM_MESSAGES"),
protect_recent=_get_env_int_optional("HEADROOM_PROTECT_RECENT"),
@ -1154,10 +1155,12 @@ def proxy(
# 2. Otherwise read HEADROOM_CODE_AWARE_ENABLED (truthy = on).
# 3. Otherwise default off — matches the prior cli/proxy.py behavior so
# existing users see no change unless they opt in.
# Default ON (coding posture; consistent with the argparse server path).
# Degrades gracefully to a no-op when tree-sitter isn't installed.
code_aware_enabled=(
bool(code_aware_flag)
if code_aware_flag is not None
else os.environ.get("HEADROOM_CODE_AWARE_ENABLED", "").strip().lower()
else os.environ.get("HEADROOM_CODE_AWARE_ENABLED", "1").strip().lower()
in ("true", "1", "yes", "on")
),
disable_kompress=disable_kompress,

View file

@ -4313,7 +4313,13 @@ def _proxy_config_from_env() -> ProxyConfig:
periodic_toin_stats_enabled=_get_env_bool("HEADROOM_PERIODIC_TOIN_STATS", True),
proxy_token=os.environ.get("HEADROOM_PROXY_TOKEN") or None,
offline=_get_env_bool("HEADROOM_OFFLINE", False),
mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_TOKEN)),
# Default mode is CACHE (Headroom's coding posture): delta-only compression
# at ~0 prefix-cache busts. HEADROOM_MODE overrides.
mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_CACHE)),
# Default savings profile is "coding" so proxy_pipeline_kwargs applies its
# posture (compress_user, protect_recent, min_tokens). HEADROOM_SAVINGS_PROFILE
# overrides.
savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding",
read_maturation=_get_env_bool("HEADROOM_READ_MATURATION", False),
read_maturation_quiesce_turns=_get_env_int("HEADROOM_READ_MATURATION_QUIESCE_TURNS", 5),
read_maturation_max_hold_turns=_get_env_int("HEADROOM_READ_MATURATION_MAX_HOLD_TURNS", 25),
@ -4324,6 +4330,12 @@ def _proxy_config_from_env() -> ProxyConfig:
def create_app_from_env() -> FastAPI:
# Seed the coding-profile defaults into the process env BEFORE reading config,
# so the uvicorn factory launch gets Headroom's out-of-box posture (cache mode,
# tool-search, dedupe, read protection, …). setdefault → explicit env wins.
from headroom.agent_savings import seed_proxy_env_defaults
seed_proxy_env_defaults()
return create_app(_proxy_config_from_env())
@ -4362,6 +4374,16 @@ def run_server(
print("ERROR: FastAPI required. Install: pip install fastapi uvicorn httpx")
sys.exit(1)
# Seed the request-time coding-profile toggles (tool-search, dedupe, read
# protection, lossless→lossy, effort-router, block-char floor) into the
# process env before serving, so downstream per-request readers pick them up.
# Done here (not in the CLI command) so unit tests that mock run_server never
# mutate os.environ. setdefault → explicit env still wins. MODE / profile are
# already resolved into `config` above via their inline defaults.
from headroom.agent_savings import seed_proxy_env_defaults
seed_proxy_env_defaults()
config = config or ProxyConfig()
code_aware_status = _get_code_aware_banner_status(config)
@ -4969,10 +4991,10 @@ if __name__ == "__main__":
protect_tool_results=frozenset(protect_tool_results)
if protect_tool_results
else frozenset(),
mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_TOKEN)),
mode=normalize_proxy_mode(_get_env_str("HEADROOM_MODE", PROXY_MODE_CACHE)),
compress_user_messages=args.compress_user_messages
or _get_env_bool("HEADROOM_COMPRESS_USER_MESSAGES", False),
savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or None,
savings_profile=os.environ.get("HEADROOM_SAVINGS_PROFILE") or "coding",
# Default 0.4 keep-ratio so the Kompress text (prose/code) path compresses
# meaningfully out of the box; HEADROOM_TARGET_RATIO overrides.
target_ratio=(

View file

@ -70,12 +70,23 @@ def test_coding_persona_protects_working_set_and_stays_visible() -> None:
env = profile.proxy_env()
assert env["HEADROOM_SAVINGS_PROFILE"] == "coding"
assert env["HEADROOM_MODE"] == "cache" # delta-only compression at ~0 prefix-cache busts
assert env["HEADROOM_PROTECT_RECENT"] == "2" # keep the active code working set verbatim
assert env["HEADROOM_MIN_TOKENS"] == "25" # low → compression is actually visible
assert env["HEADROOM_COMPRESS_USER_MESSAGES"] == "0" # no prompt mutation / cache bust
assert env["HEADROOM_COMPRESS_SYSTEM_MESSAGES"] == "0"
# Cache mode compresses the newest observation delta → compress_user must be ON.
assert env["HEADROOM_COMPRESS_USER_MESSAGES"] == "1"
assert env["HEADROOM_COMPRESS_SYSTEM_MESSAGES"] == "0" # system prompt is the hottest cache
assert env["HEADROOM_ACCURACY_GUARD"] == "strict"
assert "HEADROOM_TARGET_RATIO" not in env # unset → Kompress / ambient default decides
# Coding posture toggles seeded through the profile.
assert env["HEADROOM_TOOL_SEARCH"] == "1"
assert env["HEADROOM_DEDUPE"] == "1"
assert env["HEADROOM_LOSSLESS_THEN_LOSSY"] == "1"
assert env["HEADROOM_PROTECT_READS"] == "1"
assert env["HEADROOM_CODE_AWARE_ENABLED"] == "1"
assert env["HEADROOM_EFFORT_ROUTER"] == "0"
assert env["HEADROOM_LOSSLESS"] == "0" # lossy enabled (CCR keeps it recoverable)
assert env["HEADROOM_MIN_CHARS_FOR_BLOCK"] == "25"
def test_general_persona_has_no_positional_code_protection() -> None:
@ -89,13 +100,18 @@ def test_general_persona_has_no_positional_code_protection() -> None:
def test_personas_omit_target_ratio_in_pipeline_kwargs() -> None:
for name, expected_protect in (("coding", 2), ("general", 0)):
# coding compresses the delta observation (cache mode) → compress_user True;
# general has no positional code working set and leaves user turns intact.
for name, expected_protect, expected_compress_user in (
("coding", 2, True),
("general", 0, False),
):
kwargs = proxy_pipeline_kwargs(ProxyConfig(savings_profile=name))
assert kwargs["protect_recent"] == expected_protect
assert kwargs["read_protection_window"] == expected_protect
assert kwargs["min_tokens_to_compress"] == 25
assert kwargs["compress_user_messages"] is False
assert kwargs["compress_user_messages"] is expected_compress_user
assert kwargs["compress_system_messages"] is False
assert kwargs["force_kompress"] is False
assert "target_ratio" not in kwargs # persona never pins a keep-ratio

View file

@ -1005,6 +1005,17 @@ def test_start_proxy_does_not_apply_agent_90_defaults(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, agent_type: str
) -> None:
"""Wrapped coding agents keep agent-savings opt-in by default."""
# Clean baseline: the proxy's out-of-box coding profile seeds these into the
# process env at startup (``seed_proxy_env_defaults``), which another test in
# the shard can leave behind in ``os.environ``. This test is about what the
# WRAPPER adds, so start from an unset env rather than inheriting pollution.
for _var in (
"HEADROOM_SAVINGS_PROFILE",
"HEADROOM_TARGET_RATIO",
"HEADROOM_MAX_ITEMS",
"HEADROOM_SMART_CRUSHER_COMPACTION",
):
monkeypatch.delenv(_var, raising=False)
popen_kwargs: dict[str, object] = {}
class FakeProc:

View file

@ -338,8 +338,10 @@ class TestCLIProxyEnvVars:
assert result.exit_code == 0, result.output
assert captured_config["config"].code_aware_enabled is True
def test_code_aware_enabled_defaults_false(self, runner):
"""Without HEADROOM_CODE_AWARE_ENABLED, code-aware stays disabled in the wrapper."""
def test_code_aware_enabled_defaults_true(self, runner):
"""Without HEADROOM_CODE_AWARE_ENABLED, code-aware defaults ON (coding
posture; consistent with the argparse server path). It degrades to a no-op
when tree-sitter isn't installed, so defaulting it on is safe."""
captured_config = {}
def mock_run_server(config, **kwargs):
@ -358,7 +360,7 @@ class TestCLIProxyEnvVars:
)
assert result.exit_code == 0, result.output
assert captured_config["config"].code_aware_enabled is False
assert captured_config["config"].code_aware_enabled is True
def test_code_aware_enabled_from_cli_flag(self, runner):
"""--code-aware should enable code-aware compression in the wrapper."""