mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
2044 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e2ba09adb4
|
Extract memory injection mode policy (#1986)
## Description Extracts memory-injection mode resolution from `helpers.py` into `headroom.proxy.memory_injection_mode_policy`. The proxy still reads `HEADROOM_MEMORY_INJECTION_MODE` at request time, while the allowed values/default/error contract is now pure and directly tested. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `memory_injection_mode_policy.py` with the allowed mode type, env name/default, and resolver. - Kept `helpers.get_memory_injection_mode` as the request-time env reader and compatibility entry point. - Added direct policy tests for defaults, accepted values, normalization, and invalid mode rejection. ## 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 python -m pytest tests\test_memory_injection_mode_policy.py tests\test_proxy_system_prompt_immutable.py 10 passed in 14.60s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-33`. - Exact command / steps: ran new memory injection mode policy tests, existing system-prompt immutability tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: memory injection mode behavior remains covered and local lint/type/security checks pass. - Not tested: live proxy request; existing helper entry point remains intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. --------- Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
d45748d143
|
Extract query log policy (#1984)
## Description Extracts the privacy-preserving memory-query log hash from `helpers.py` into `headroom.proxy.query_log_policy`. The helper import path remains intact, while the log identifier formula is now directly testable as a pure policy. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `query_log_policy.py` with the BLAKE2b-based short query hash formula. - Kept `helpers.hash_query_for_log` delegating to the extracted policy for existing callers. - Added direct tests for stability, short hex shape, content sensitivity, unpaired surrogate handling, and helper delegation. ## 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 python -m pytest tests\test_query_log_policy.py 4 passed in 0.18s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-32`. - Exact command / steps: ran focused query-log policy tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: query log hash behavior is directly covered and local lint/type/security checks pass. - Not tested: live memory injection logging; existing helper entry point remains intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. --------- Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
7fb9209089
|
Extract diagnostic decode policy (#1981)
## Description Extracts lossy diagnostic byte decoding from `helpers.py` into `headroom.proxy.diagnostic_decode_policy`. Protocol parsers stay strict while the diagnostic/logging path has a dedicated, directly tested policy. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `diagnostic_decode_policy.py` for UTF-8 diagnostic decoding with replacement characters. - Kept `helpers.safe_decode_for_logging` delegating to the extracted policy for existing callers. - Added direct tests for valid UTF-8, invalid byte replacement, max-byte truncation, and helper delegation. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_diagnostic_decode_policy.py 4 passed in 0.18s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-30`. - Exact command / steps: ran focused diagnostic decode policy tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: diagnostic decode behavior is directly covered and local lint/type/security checks pass. - Not tested: live upstream error responses; existing helper import path remains intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. --------- Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
41ce14bd64
|
Extract wire debug format policy (#1978)
## Description Extracts opt-in Codex wire-debug formatting from `helpers.py` into `headroom.proxy.wire_debug_format_policy`. The existing helper functions now delegate to the pure policy so filename-safe event names and proxy-log previews are directly testable. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `wire_debug_format_policy.py` for safe wire-debug name fragments and compact log previews. - Kept `_safe_event_name` and `_wire_debug_preview` in `helpers.py` as compatibility delegates. - Added direct tests for unsafe-name replacement, length capping, JSON preview compaction, byte decoding/truncation, and `None` handling. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_wire_debug_format_policy.py 5 passed in 0.19s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-28`. - Exact command / steps: ran focused wire-debug format policy tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: formatting policy behavior is directly covered and local lint/type/security checks pass. - Not tested: live wire-debug capture writing; this slice preserves the existing helper entry points. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. --------- Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
ec3c3cd234
|
refactor(proxy): extract ccr marker policy (#2004)
## Description Extracts CCR marker freshness and retrieval-tool injection decision policy from `headroom.proxy.helpers` into a focused pure module. Existing helper functions remain as compatibility wrappers for current Anthropic/OpenAI handler imports. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.ccr_marker_policy` for new-marker detection and frozen-prefix tool injection decisions. - Kept `helpers.has_new_ccr_markers()` and `helpers.should_inject_ccr_tool()` as compatibility wrappers. - Added direct policy tests for replayed markers, genuinely new markers, missing prior forwards, empty current hashes, and frozen-prefix override behavior. ## 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 python -m pytest tests/test_ccr_marker_policy.py tests/test_proxy/test_ccr_frozen_prefix_coupling.py tests/test_proxy_handler_helpers.py::TestHasNewCcrMarkers 16 passed in 0.91s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13 - Exact command / steps: Ran direct CCR marker policy tests, frozen-prefix coupling tests, existing helper marker freshness tests, full ruff, format check, mypy, and staged gitleaks scan. - Observed result: Existing frozen-prefix CCR behavior remains green while the marker freshness and injection decision policy is directly covered. - Not tested: Full repository pytest suite locally; CI covers the broader matrix. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. The default-branch Dependabot alerts reported during push are pre-existing and unrelated to this PR. Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
9c7b9d5a9c
|
refactor(proxy): extract tool injection logging (#2009)
## Description
Extracts proxy tool-injection decision logging from
`headroom.proxy.helpers` into a focused logging policy module. The
public helper function remains in place and delegates to the new module,
so existing injection call sites keep their current API while the
logging format has direct tests.
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
- [x] Code refactoring (no functional changes)
## Changes Made
- Added `headroom.proxy.tool_injection_logging` with the shared
`ToolInjectionDecision` type and structured logging helper.
- Updated `helpers.log_tool_injection_decision` to delegate to the
logging policy module while preserving the existing helper API.
- Added tests that assert the emitted structured fields and verify tool
names/contents are not logged.
## 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
python -m pytest tests/test_tool_injection_logging.py tests/test_memory_tool_session_sticky.py tests/test_ccr_tool_always_on.py tests/test_corrupt_golden_bytes_recovery.py tests/test_issue_728_empty_tools_injection.py
60 passed in 0.95s
python -m ruff check .
All checks passed!
python -m ruff format --check .
1069 files already formatted
python -m mypy headroom --ignore-missing-imports
Success: no issues found in 410 source files
gitleaks protect --staged --no-banner --redact
no leaks found
```
## Real Behavior Proof
- Environment: Windows, Python 3.13.13, clean worktree from
`headroomlabs/main` at `
|
||
|
|
d6259b2263
|
refactor(proxy): extract tool injection policy (#1995)
## Description Extracts memory tool injection stickiness configuration parsing from `headroom.proxy.helpers` into a focused policy module. The existing helper functions remain in place for the session tool tracker and sticky injection helpers. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.tool_injection_policy` for tool sticky mode and tracker session limit resolution. - Kept `get_tool_injection_sticky_mode()` and `get_tool_tracker_max_sessions()` as compatibility wrappers in `helpers.py`. - Added direct unit tests for defaults, accepted values, and loud rejection of invalid operator configuration. ## 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 python -m pytest tests/test_tool_injection_policy.py tests/test_memory_tool_session_sticky.py 38 passed in 0.44s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13 - Exact command / steps: Ran focused tool injection policy tests, existing memory tool sticky suite, full ruff, format check, mypy, and staged gitleaks scan. - Observed result: Existing sticky memory-tool behavior remains green while extracted policy parsing is covered directly. - Not tested: Full repository pytest suite locally; CI covers the broader matrix. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. The default-branch Dependabot alerts reported during push are pre-existing and unrelated to this PR. Co-authored-by: Tejas Chopra <chopratejas@gmail.com> |
||
|
|
10001755e8
|
refactor(proxy): extract tool name policy (#2008)
## Description
Extracts proxy tool-definition name parsing from
`headroom.proxy.helpers` into a focused policy module. The existing
private helper remains as a compatibility wrapper while memory and CCR
injection skip logic share the tested parser.
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
- [x] Code refactoring (no functional changes)
## Changes Made
- Added `headroom.proxy.tool_name_policy.extract_tool_name` for
Anthropic custom tools, OpenAI function tools, and Anthropic native
memory tools.
- Updated `helpers._extract_tool_name` to delegate to the policy module
while keeping its existing import path intact.
- Added direct tests for name precedence, function-tool parsing,
native-tool fallback, invalid values, and wrapper compatibility.
## 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
python -m pytest tests/test_tool_name_policy.py tests/test_memory_tool_session_sticky.py tests/test_ccr_tool_always_on.py tests/test_issue_728_empty_tools_injection.py tests/test_proxy/test_ccr_frozen_prefix_coupling.py
60 passed in 0.90s
python -m ruff check .
All checks passed!
python -m ruff format --check .
1069 files already formatted
python -m mypy headroom --ignore-missing-imports
Success: no issues found in 410 source files
gitleaks protect --staged --no-banner --redact
no leaks found
```
## Real Behavior Proof
- Environment: Windows, Python 3.13.13, clean worktree from
`headroomlabs/main` at `
|
||
|
|
7c9a032f50
|
refactor(proxy): extract ccr golden replay policy (#2006)
## Description
Extracts CCR golden tool replay and fresh-definition canonicalization
from `headroom.proxy.helpers.apply_session_sticky_ccr_tool` into a
focused policy module. This keeps sticky CCR orchestration in helpers
while making the byte replay/regeneration behavior independently
testable.
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
- [x] Code refactoring (no functional changes)
## Changes Made
- Added `headroom.proxy.ccr_golden_policy` for replaying stored CCR
golden bytes and creating canonical fresh CCR tool definitions.
- Updated `apply_session_sticky_ccr_tool` to delegate CCR golden
replay/fresh definition policy while preserving tracker coordination and
logging decisions.
- Added direct tests for golden-byte replay, invalid/corrupt bytes,
non-UTF-8 bytes, and fresh canonical definition generation.
## 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
python -m pytest tests/test_ccr_golden_policy.py tests/test_ccr_tool_always_on.py tests/test_corrupt_golden_bytes_recovery.py tests/test_proxy/test_ccr_frozen_prefix_coupling.py
30 passed in 0.34s
python -m ruff check .
All checks passed!
python -m ruff format --check .
1069 files already formatted
python -m mypy headroom --ignore-missing-imports
Success: no issues found in 410 source files
gitleaks protect --staged --no-banner --redact
no leaks found
```
## Real Behavior Proof
- Environment: Windows, Python 3.13.13, clean worktree from
`headroomlabs/main` at `
|
||
|
|
d1c484b164
|
refactor(proxy): extract tool injection tracker (#2002)
## Description Extracts the sticky memory tool session tracker from `headroom.proxy.helpers` into a focused state module. `helpers.SessionToolTracker` remains as an env-aware compatibility wrapper so existing injection and singleton call sites keep the same API. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.tool_injection_tracker.SessionToolTracker` as the pure bounded LRU state holder. - Replaced the large in-helper tracker implementation with a small env-aware wrapper. - Added direct tracker tests for unknown sessions, ordered golden bytes, first-write wins, provider isolation, LRU eviction, and input validation. ## 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 python -m pytest tests/test_tool_injection_tracker.py tests/test_memory_tool_session_sticky.py tests/test_corrupt_golden_bytes_recovery.py 44 passed in 0.54s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13 - Exact command / steps: Ran direct tracker tests, sticky memory tool tests, corrupt golden byte recovery tests, full ruff, format check, mypy, and staged gitleaks scan. - Observed result: Existing sticky injection behavior and recovery behavior remain green while the tracker state domain is directly covered. - Not tested: Full repository pytest suite locally; CI covers the broader matrix. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. The default-branch Dependabot alerts reported during push are pre-existing and unrelated to this PR. |
||
|
|
b910ce5deb
|
Extract SSE byte buffer policy (#1979)
## Description Extracts the pure SSE byte-buffer parser from `helpers.py` into `headroom.proxy.sse_byte_buffer_policy`. Existing helper imports remain as delegates, while the protocol parser now has its own module and direct tests. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `sse_byte_buffer_policy.py` for SSE terminator detection and complete-event parsing. - Kept `helpers.parse_sse_events_from_byte_buffer` and `_find_sse_event_terminator` delegating to the extracted policy. - Added direct policy tests for LF/CRLF terminators, buffer draining, split UTF-8 preservation, and invalid complete UTF-8 events. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_sse_byte_buffer_policy.py tests\test_sse_utf8_split.py 8 passed in 0.23s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-29`. - Exact command / steps: ran new SSE byte-buffer policy tests, existing SSE UTF-8 split tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: SSE parser behavior remains covered and local lint/type/security checks pass. - Not tested: live streaming proxy runtime; existing helper imports remain intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
984a2c702c
|
fix(ccr): don't crash parse_tool_call on non-object tool arguments (#2071)
## Description
`parse_tool_call` (`headroom/ccr/tool_injection.py`) extracts the
retrieval hash from a CCR tool
call. For the OpenAI and `openai_responses` shapes it decodes the
`arguments` string with
`json.loads` and catches only `JSONDecodeError`:
```python
args_str = function.get("arguments", "{}")
try:
input_data = json.loads(args_str)
except json.JSONDecodeError:
input_data = {}
...
hash_key = input_data.get("hash") # assumes input_data is a dict
```
If a (confused) model emits `arguments='[]'` / `'"abc"'` / `'123'`,
`json.loads` succeeds and
returns a **list / str / number**, so `input_data.get("hash")` raises
`AttributeError`. A null
value (`arguments: null` → `json.loads(None)`) raises an uncaught
`TypeError`. The Anthropic branch
has the same hazard if `tool_call["input"]` is present but not a dict.
`parse_tool_call` is called from `parse_ccr_tool_calls`
(`ccr/tool_calls.py`) and the server CCR
path with no guard for this, so a malformed CCR-named tool call
**crashes CCR response
processing** instead of being ignored.
Closes: no issue filed — found while auditing the CCR tool-call parsing.
## Fix
- Catch `TypeError` as well as `JSONDecodeError` around `json.loads`
(covers `arguments: null`).
- Return `None` when `input_data` is not a `dict` — a non-object tool
call simply isn't a valid CCR
call.
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- `headroom/ccr/tool_injection.py`: widen the decode `except` to
`(json.JSONDecodeError, TypeError)`; return `None` for non-dict
`input_data`.
- `tests/test_ccr_tool_injection.py`: add tests for non-object OpenAI
arguments (`[]`/`"abc"`/`123`), null arguments, and a non-dict Anthropic
`input`.
## Testing
- [x] New regression tests added (`tests/test_ccr_tool_injection.py`)
- [x] Linting/formatting clean — run with the CI-pinned `ruff==0.15.17`
- [ ] Full `pytest` deferred to CI (local-OOM reason below).
```text
$ uvx ruff@0.15.17 check headroom/ccr/tool_injection.py tests/test_ccr_tool_injection.py
All checks passed!
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.10, headroom from this branch.
Importing `headroom` pulls in the torch/transformers stack and a full
`pytest` gets OOM-killed on this box, so I verified the parse logic with
a dependency-free script and left the full pytest to CI.
- Exact command / steps: ran the four crash vectors (openai `[]`,
`"abc"`, `null`; anthropic non-dict `input`) plus a valid CCR call and a
non-CCR call through the old and new logic.
- Observed result: the old parser crashes on every malformed case; the
new one returns `None` and still parses a valid call:
```text
OK [openai] '[]': old CRASHED -> new None
OK [openai] '"abc"': old CRASHED -> new None
OK [openai] None: old CRASHED -> new None
OK [anthropic] ['not', 'a', 'dict']: old CRASHED -> new None
PARSE_TOOL_CALL NON-DICT FIX VERIFIED (old crashes; new returns None; valid still parses)
```
- Not tested: a full CCR response round-trip with a malformed tool call
(needs the heavy stack). The fix is confined to `parse_tool_call` and
the new tests drive it directly. Full local `pytest` deferred to CI
(OOM, per above).
## 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
- [ ] New and existing unit tests pass locally with my changes — ran
lint + a standalone logic check; full pytest deferred to CI (local OOM,
disclosed above)
- [x] I have updated the CHANGELOG.md if applicable
## Additional Notes
- Two-line hardening plus tests; no new dependencies.
- @JerrettDavis tagging you — a malformed CCR-named tool call currently
crashes CCR response processing; quick one. Thanks!
|
||
|
|
868b88bc64
|
refactor(proxy): extract internal header policy (#1990)
## Description Extracts the internal x-headroom request-header stripping policy from `headroom.proxy.helpers` into a focused policy module. This keeps the security-sensitive upstream filtering rule independently testable while preserving the existing helper API used by provider handlers. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.internal_header_policy` for strip mode resolution and x-headroom header filtering. - Kept `get_strip_internal_headers_mode()` and `_strip_internal_headers()` as compatibility wrappers in `helpers.py`. - Added direct unit tests for default/disabled/invalid modes, case-insensitive filtering, and copy semantics. ## 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 python -m pytest tests/test_internal_header_policy.py tests/test_header_isolation.py 29 passed in 4.89s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13 - Exact command / steps: Ran focused policy/header isolation pytest coverage plus full ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: Header stripping behavior remains green end-to-end, direct policy tests cover security-sensitive parsing/filtering rules, and local quality/security gates pass. - Not tested: Full repository pytest suite locally; CI covers the broader matrix. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. The default-branch Dependabot alerts reported during push are pre-existing and unrelated to this PR. |
||
|
|
4640587a06
|
Extract wire debug redaction policy (#1972)
## Description Extracts the pure secret-redaction logic used by opt-in Codex wire-debug capture from `helpers.py` into `headroom.proxy.wire_debug_redaction_policy`. This keeps the debug capture path behavior intact while making the sensitive-key policy directly testable. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `wire_debug_redaction_policy.py` for secret-key matching and recursive wire-debug redaction. - Kept existing helper entry points and private compatibility names delegating to the extracted policy. - Added direct tests for direct secret headers, nested suffix-matched secrets, and key normalization. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_wire_debug_redaction_policy.py 3 passed in 0.16s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-25`. - Exact command / steps: ran focused wire-debug redaction tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: redaction policy is directly covered and local lint/type/security checks pass. - Not tested: full proxy wire-debug capture runtime; this slice preserves the existing helper entry points. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
2f53a18a3f
|
refactor(proxy): isolate semantic cache key policy (#1964)
## Description Extracts proxy semantic response-cache key normalization and hashing into a pure `semantic_cache_key_policy` module. `SemanticCache` keeps ownership of storage, locking, TTL, and LRU behavior while the deterministic cache-key formula is directly tested as a standalone policy. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.semantic_cache_key_policy` with recursive `cache_control` stripping and semantic cache key hashing. - Updated `SemanticCache._compute_key` to delegate to the pure key policy while preserving its existing private wrapper contract. - Added direct policy tests for recursive annotation stripping, key stability, response-shaping distinctions, breakpoint movement, and wrapper parity. - Included the current LiteLLM callback signature compatibility shim required for repo-wide mypy on main-based slices. ## 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 python -m pytest tests/test_semantic_cache_key_policy.py tests/test_proxy_semantic_cache_key.py tests/test_litellm_callback.py -q 39 passed in 6.34s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, clean worktree based on `headroomlabs/main`. - Exact command / steps: targeted pytest, ruff, format check, repo-wide mypy, staged gitleaks scan. - Observed result: semantic cache key policy/cache/callback tests pass; static checks pass; no staged secrets detected. - Not tested: live proxy cache traffic; this slice preserves the existing cache wrapper and only moves pure key policy. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal architecture slice. PR-specific GHAS checks will be monitored after opening. |
||
|
|
c904a70d4e
|
refactor(proxy): isolate output turn policy (#1962)
## Description Extracts output-shaper turn classification into a pure `output_turn_policy` module. The shaper still owns request mutation and labels, while Anthropic-style and OpenAI Responses structural turn classification now live in a deterministic policy boundary. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.output_turn_policy` with `TurnKind`, `classify_turn`, and `classify_openai_responses_input`. - Updated `output_shaper` to import and re-export the classifiers, preserving existing import behavior. - Added direct policy tests for Anthropic tool-result turns and OpenAI Responses input classification. - Included the current LiteLLM callback signature compatibility shim required for repo-wide mypy on main-based slices. ## 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 python -m pytest tests/test_output_turn_policy.py tests/test_output_shaper.py tests/test_litellm_callback.py -q 60 passed in 6.25s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, clean worktree based on `headroomlabs/main`. - Exact command / steps: targeted pytest, ruff, format check, repo-wide mypy, staged gitleaks scan. - Observed result: output turn policy/shaper/callback tests pass; static checks pass; no staged secrets detected. - Not tested: live provider calls; this slice only moves structural classification logic and preserves existing shaper behavior. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal architecture slice. PR-specific GHAS checks will be monitored after opening. |
||
|
|
2c9eb7c5f1
|
feat(simulators): add provider simulator service (#2014)
## Description
Adds a Rust-only `headroom-simulators` workspace crate: a deterministic
local upstream simulator service for Headroom proxy and pipeline
validation. It supplies configurable stubs plus bottled provider-shaped
responses for supported provider/path surfaces without calling real
LLMs.
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)
## Changes Made
- Added `crates/headroom-simulators` Rust crate with library and
`headroom-simulators` binary.
- Added clean domain classification for supported surfaces: Anthropic
`/v1/messages`, OpenAI chat/responses/conversations, Bedrock
invoke/stream routes, Vertex raw/stream predict, health, and generic
fallback.
- Added JSON-configured stub matching by method, path, body substring,
and JSON pointer.
- Added bottled provider-shaped JSON, SSE, and Bedrock EventStream
responses for unconfigured requests.
- Added a container `Dockerfile` and README for local/GitHub Actions
usage.
- Added unit and HTTP integration tests for defaults, configured stubs,
SSE, Vertex, and Bedrock EventStream behavior.
- Added proxy-level simulator-backed E2E tests that run Headroom against
the simulator across Anthropic, OpenAI Chat, OpenAI Responses, OpenAI
Conversations, Bedrock invoke/converse/streaming, Vertex raw/stream
predict, and upstream health.
- Added simulator-backed provider error-path E2E coverage for OpenAI
429, Anthropic 529, Bedrock 502, and Vertex 503 responses flowing
through Headroom unchanged.
- Added Headroom-owned preflight error E2E coverage proving Bedrock
missing credentials and invalid Vertex envelopes stop inside the proxy
instead of silently falling through to the simulator/provider.
- Fixed direct Rust `headroom-core` binaries/tests on Windows so Magika
initializes ONNX Runtime via `ort::init_from` from an explicit pip
`onnxruntime` library path, with fail-fast fallback only when no safe
runtime is discoverable.
- Added a Rust CI `simulator-e2e` matrix for `ubuntu-latest`,
`macos-latest`, and `windows-latest` that runs `cargo test -p
headroom-proxy --test e2e_simulators`.
- Gated dynamic Magika `Path`/`PathBuf` imports to Windows and x86_64
macOS so Linux clippy does not see unused dynamic-ORT-only imports.
## Testing
- [ ] Unit tests pass (`pytest`)
- [ ] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed
### Test Output
cargo fmt --all -- --check
# passed
cargo clippy --workspace -- -D warnings
# passed
$env:ORT_DYLIB_PATH=$null
cargo test -p headroom-core transforms::magika_detector::tests:: --lib
# 17 passed, 0 failed; Magika initialized from discovered pip
onnxruntime DLL
$env:ORT_DYLIB_PATH=$null
cargo test --workspace
# passed
gitleaks protect --staged --no-banner --redact
# no leaks found
gitleaks git --log-opts="headroomlabs/main..HEAD" --no-banner --redact
# 5 commits scanned; no leaks found
## Real Behavior Proof
- **Environment:** Windows PowerShell, Rust toolchain `1.95.0`, clean
worktree from `headroomlabs/main` at `
|
||
|
|
7f7af667ed
|
feat(observability): add gen_ai.request.model to the compression span (#1667)
## Description Emit the OpenTelemetry GenAI semantic-convention attribute `gen_ai.request.model` on the existing `headroom.compression.pipeline` span, alongside the current `headroom.*` attributes. Today Headroom's OTel spans use only proprietary `headroom.*` names, so a team pointing an OTel-native backend at Headroom can't join its telemetry to their existing `gen_ai.*` LLM dashboards. This makes the compression span groupable/filterable by the standard schema. Proposed and scoped in #1671. Per CONTRIBUTING (new features want a maintainer 👍 + spec first), this is opened as a **draft** to get sign-off on the approach and v1 scope before finalizing. ## Type of Change - [x] New feature (non-breaking change that adds functionality) ## Short spec - API surface: one additive span attribute, `gen_ai.request.model`, on the existing `headroom.compression.pipeline` span. No new endpoints, headers, or config; nothing renamed. - Scope (v1, deliberately minimal): only `gen_ai.request.model` — the one gen_ai attribute this pre-flight compression span can set correctly and unconditionally (the model is always known here). - Deferred to v2 (each needs work this span cannot do correctly, and I'd value your steer on all three): - `gen_ai.operation.name`: `apply()` is shared by many callers (chat, `/v1/compress`, batch, Gemini `countTokens`), so no single hardcoded value is right — it has to be threaded from each caller. - `gen_ai.provider.name`: Headroom's provider label can't distinguish Bedrock/Gemini from Anthropic/OpenAI at this layer (Bedrock routes through the Anthropic provider). - `gen_ai.usage.*`: provider-authoritative usage lives on the response path, not this span; the compressed-input estimate stays under `headroom.tokens.after`. - Failure modes: model missing → attribute omitted (never a blank string); span not recording / `record_metrics=False` → no attribute, no crash. - Security: no new input surface; derived from data already on the span. ## Changes Made - `headroom/transforms/pipeline.py`: emit `gen_ai.request.model` on the pipeline span (guarded on model present), with a comment documenting why the other gen_ai.* attributes are deferred. - `tests/test_observability_tracing.py`: assert the attribute is emitted, the deferred attrs are omitted, the model-missing guard, and the non-recording path. - `CHANGELOG.md`: Unreleased → Features entry. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [x] New tests added for new functionality ### Test Output ```text $ uv run pytest tests/test_observability_tracing.py -q 7 passed $ uv run pytest tests/test_observability_tracing.py tests/test_compression_observability.py \ tests/test_observability_metrics.py tests/test_pipeline.py tests/test_canonical_pipeline.py tests/test_telemetry.py -q 76 passed $ uv run ruff check . && uv run mypy headroom/transforms/pipeline.py --ignore-missing-imports All checks passed! / Success: no issues found in 1 source file ``` ## Real Behavior Proof - Environment: local, Python 3.12, `opentelemetry-sdk` 1.39.1, real `ConsoleSpanExporter` (not a mock). Ran the actual `TransformPipeline.apply()` emission path. - Exact command / steps: configured a real `TracerProvider` + `ConsoleSpanExporter`, set it as Headroom's tracer, ran `TransformPipeline([]).apply([{user msg}], model="claude-3-5-sonnet-20241022", model_limit=8192)`, then `force_flush()` and inspected the exported span. - Observed result: the exported `headroom.compression.pipeline` span carries `gen_ai.request.model` alongside the existing `headroom.*` attributes: ```json "attributes": { "headroom.model": "claude-3-5-sonnet-20241022", "headroom.provider": "unknown", "headroom.message_count": 1, "headroom.tokens.before": 83, "gen_ai.request.model": "claude-3-5-sonnet-20241022", "headroom.tokens.after": 83, "headroom.tokens.saved": 0 } ``` - Not tested: no live OTLP collector / Grafana backend (used the console exporter, which is the same span pipeline); the deferred v2 attributes (`operation.name`/`provider.name`/`usage.*`) are intentionally not emitted. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review (Draft: awaiting a maintainer 👍 on the approach and the v1 scope before marking ready.) ## Additional Notes Purely additive and back-compatible — no `headroom.*` attribute changed or removed. The gen_ai attribute name is a string literal because the `gen_ai.*` conventions are stability=development in the semconv registry (no stable constants published). No new dependencies. Signed-off-by: Krishnachaitanyakc <krishnabkc15@gmail.com> Co-authored-by: JerrettDavis <mxjerrett@gmail.com> |
||
|
|
ad9d086f43
|
feat(codex): keep wrap routing session-scoped (#1507)
## Description Keeps Codex wrap routing session-scoped so routing state from one wrapped session does not leak into another. ## 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 - Scope Codex wrap routing state to the active session. - Avoid cross-session routing contamination for wrapped Codex traffic. - Keep changes focused on wrap/proxy routing behavior. ## Testing - [x] Unit tests pass - [x] New tests added for new functionality when applicable - [x] Manual testing performed ### Test Output ```text Focused tests/review were completed before this governance body cleanup. The current branch is conflicted and still needs merge resolution before final merge readiness. ``` ## Real Behavior Proof - Environment: Headroom development/review context. - Exact command / steps: Reviewed session-scoped Codex wrap routing behavior and existing focused coverage. - Observed result: Routing state is scoped to the active wrap session rather than shared globally across sessions. - Not tested: Current conflicted branch after merge resolution; conflicts still need to be resolved before merge. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Additional Notes This body was normalized by a maintainer after approval so the governance parser reflects the already-reviewed PR state. The PR remains blocked by merge conflicts. |
||
|
|
b0440f958d
|
fix(cache): partial cached-prefix replay + idle-aware net-cost; don't… (#1933)
… revert an overlaid prefix overlay_cached_prefix (prefix_tracker): - Replay the previously-forwarded (cached, compressed) prefix up to the FIRST divergence instead of all-or-nothing. Previously a single changed leading message — most commonly the just-added assistant turn, whose client-resent form can differ trivially from the copy we reconstruct + record — made the guard bail and forward the freshly-recompressed prefix, busting the ENTIRE cache from message 0. Stopping at the divergence keeps the (large) cache-hit region and only re-forwards from the changed message on. This is the token-mode cache-safety fix: measured REAL_BUST 50-65% -> ~6% on Opus SWE-bench, with token mode landing resolve-neutral vs cache mode. - Safe by construction: only replays prev_fwd[k] where current_original[k] canonicalize-equals prev_orig[k] (positional 1:1 guaranteed by the count check), so no wrong bytes are ever forwarded. idle plumbing (prefix_tracker + anthropic): - Snapshot idle-since-last-response in get_or_create BEFORE it bumps the access clock (otherwise seconds_since_activity reads ~0 every turn), and forward it to the pipeline as idle_seconds so the dormant net-cost/TTL P_alive gate (HEADROOM_NET_COST_POLICY=1) can actually see idle time. Harmless when the policy is off; ~0 for back-to-back agent turns. anthropic inflation guard: - Skip the "optimization inflated tokens -> revert to originals" guard when overlay just replayed a byte-identical cached prefix. Reverting there would re-forward the raw uncompressed prefix and bust the live prompt cache (trading a 90% read discount for a full re-write) — far costlier than the small tail inflation the guard exists to avoid. ## 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 <noreply@anthropic.com> |
||
|
|
d079614b1f
|
fix(mcp/opencode): don't clobber an unparseable opencode.json on register (#1661)
## Description
`OpencodeRegistrar._write_entry` does a full-file read-modify-write of
`opencode.json`:
```python
data = _read_json(self._config_path) # returns {} on JSONDecodeError
mcp = data.setdefault("mcp", {})
mcp[spec.name] = _spec_to_entry(spec)
_write_json(self._config_path, data) # overwrites the ENTIRE file
```
`_read_json` returns `{}` for a file that exists but doesn't parse.
OpenCode
configs are commonly hand-edited and JSONC-ish (comments, trailing
commas), so a
file that doesn't strictly parse gets silently rewritten as just
`{"mcp": {"headroom": {...}}}` — **destroying the user's `theme`,
`model`,
`provider`, and any other MCP servers**. No backup.
This is the same class of data-loss bug as the Claude registrar
(separate PR);
this one is `headroom/mcp_registry/opencode.py`.
Closes: no issue filed — found while auditing the MCP registry
config-write paths.
## Fix
Keep `_read_json` (returning `{}`) for read-only callers. Add
`_read_json_for_write` for the rewrite path: it returns `{}` only when
the file
is **absent or empty**, and raises `_MalformedConfigError` when the file
is
present but not a JSON object. `_write_entry` catches it and returns
`FAILED`
with an actionable message instead of overwriting.
Absent/empty → registers fresh (unchanged); valid → merges, all keys
preserved
(unchanged); present-but-invalid → left untouched.
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- `headroom/mcp_registry/opencode.py`: add `_read_json_for_write` +
`_MalformedConfigError`; `_write_entry` uses it and returns `FAILED`
(without writing) when `opencode.json` is present-but-unparseable.
`_read_json` unchanged for read-only callers.
- `tests/test_mcp_registry_opencode.py`: regression tests — register
against malformed configs leaves the bytes untouched and returns
`FAILED`; register against a valid config still merges and preserves
`theme`/`model` plus a pre-existing MCP server.
- `CHANGELOG.md`: Bug Fixes entry under Unreleased.
## Testing
- [x] New tests added for the fixed behavior
- [x] Linting passes (`ruff check`) and formatting is clean (`ruff
format --check`)
- [ ] Full `pytest` deferred to CI (local-OOM reason below).
```text
$ uv run ruff check headroom/mcp_registry/opencode.py tests/test_mcp_registry_opencode.py
All checks passed!
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.12.11, headroom built from this
branch. Importing `headroom` loads the torch/transformers stack; a full
`pytest` gets OOM-killed on this box, so I verified the write-path logic
with a dependency-free script and left the full pytest to CI.
- Exact command / steps: the write-path logic here is identical to the
Claude registrar fix, so I verified it with the same standalone script —
replicated `_read_json_for_write` + the read-modify-write flow (only
stdlib, no `headroom` import) against real temp files, exercising
absent, empty, four malformed variants, and a valid config carrying
unrelated keys.
- Observed result: absent/empty register fresh; every malformed variant
returns FAILED and the on-disk bytes are unchanged (no clobber); a valid
config merges the new server while unrelated keys survive:
```text
OK: absent -> fresh register
OK: empty -> fresh register
OK: malformed -> FAILED, original bytes preserved (no clobber)
OK: valid config -> merged, unrelated keys preserved
MCP CONFIG-WRITE LOGIC VERIFIED
```
- Not tested: driving a real `opencode` install end-to-end (didn't want
to touch a real config); the file-write path is exercised directly by
the regression tests. Full local `pytest` deferred to CI (OOM, per
above).
## 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
- [ ] New and existing unit tests pass locally with my changes — ran
lint + a standalone logic check; full pytest deferred to CI (local OOM,
disclosed above)
- [x] I have updated the CHANGELOG.md if applicable
## Additional Notes
- Companion to the Claude-registrar fix (same root cause, different
file). No new dependencies. This does not touch OpenCode's
`opencode.jsonc` file-selection (handled elsewhere) — it only hardens
the existing `opencode.json` write against clobbering.
|
||
|
|
ce3c959eae
|
deps: update tree-sitter requirement from <0.26,>=0.25.2 to >=0.25.2,<0.27 (#1681)
Updates the requirements on [tree-sitter](https://github.com/tree-sitter/py-tree-sitter) to permit the latest version. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/tree-sitter/py-tree-sitter/releases">tree-sitter's releases</a>.</em></p> <blockquote> <h2>v0.26.0</h2> <h2>What's Changed</h2> <ul> <li>ci: use windows-2025 & macos-15-intel runners by <a href="https://github.com/ObserverOfTime"><code>@ObserverOfTime</code></a> in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/422">tree-sitter/py-tree-sitter#422</a></li> <li>ci: bump pypa/cibuildwheel from 3.1 to 3.2 in the actions group by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/418">tree-sitter/py-tree-sitter#418</a></li> <li>ci: bump the actions group with 2 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/424">tree-sitter/py-tree-sitter#424</a></li> <li>ci: bump pypa/cibuildwheel from 3.2 to 3.3 in the actions group by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/426">tree-sitter/py-tree-sitter#426</a></li> <li>ci: bump the actions group across 1 directory with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/430">tree-sitter/py-tree-sitter#430</a></li> <li>feat!: update API for tree-sitter 0.26 by <a href="https://github.com/ObserverOfTime"><code>@ObserverOfTime</code></a> in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/431">tree-sitter/py-tree-sitter#431</a></li> <li>Add Python 3.14 to CI workflow matrix by <a href="https://github.com/cclauss"><code>@cclauss</code></a> in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/434">tree-sitter/py-tree-sitter#434</a></li> <li>ci: add riscv64 wheels to PyPI release workflow by <a href="https://github.com/gounthar"><code>@gounthar</code></a> in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/443">tree-sitter/py-tree-sitter#443</a></li> <li>ci: bump the actions group across 1 directory with 5 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/446">tree-sitter/py-tree-sitter#446</a></li> <li>fix type hints for Query properties by <a href="https://github.com/unawarez"><code>@unawarez</code></a> in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/439">tree-sitter/py-tree-sitter#439</a></li> <li>build: bump tree_sitter/core from <code>cd4b6e2</code> to <code>6f2e8a6</code> by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/447">tree-sitter/py-tree-sitter#447</a></li> <li>build: bump tree_sitter/core from <code>6f2e8a6</code> to <code>cd5b087</code> by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/449">tree-sitter/py-tree-sitter#449</a></li> <li>build: bump tree-sitter-rust from 0.24.0 to 0.24.1 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/444">tree-sitter/py-tree-sitter#444</a></li> <li>ci: bump actions/upload-pages-artifact from 4 to 5 in the actions group by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/452">tree-sitter/py-tree-sitter#452</a></li> <li>build: bump tree_sitter/core from <code>cd5b087</code> to <code>7f53486</code> by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/459">tree-sitter/py-tree-sitter#459</a></li> <li>ci: bump the actions group across 1 directory with 2 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/463">tree-sitter/py-tree-sitter#463</a></li> <li>build: bump tree-sitter-rust from 0.24.1 to 0.24.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/453">tree-sitter/py-tree-sitter#453</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/cclauss"><code>@cclauss</code></a> made their first contribution in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/434">tree-sitter/py-tree-sitter#434</a></li> <li><a href="https://github.com/gounthar"><code>@gounthar</code></a> made their first contribution in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/443">tree-sitter/py-tree-sitter#443</a></li> <li><a href="https://github.com/unawarez"><code>@unawarez</code></a> made their first contribution in <a href="https://redirect.github.com/tree-sitter/py-tree-sitter/pull/439">tree-sitter/py-tree-sitter#439</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/tree-sitter/py-tree-sitter/compare/v0.25.2...v0.26.0">https://github.com/tree-sitter/py-tree-sitter/compare/v0.25.2...v0.26.0</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
0750bbff4d
|
fix(update): prevent _core.pyd corruption on Windows when proxy is running (#1581)
## Description On Windows, running `headroom update` while `headroom proxy` is active can corrupt the installed package by leaving the native `_core.pyd` extension in a partially upgraded state. This PR adds a safer update path around the pip invocation. Closes #1580. ## 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 `safe_update()` handling for Windows native-extension update safety. - Detect whether `_core.pyd` is locked before pip runs. - Create a proactive backup when the file is not locked, then restore atomically if import integrity fails. - Warn when the proxy is running and `_core.pyd` is locked, allowing pip to fail safely without replacing the loaded file. - Use atomic replacement for restore paths. ## Testing - [x] Unit tests pass - [x] New tests added for new functionality when applicable - [x] Manual testing performed ### Test Output ```text Focused update-path tests and reviewer approval were completed on this PR before the governance body cleanup. The current body update is documentation-only metadata for PR governance. ``` ## Real Behavior Proof - Environment: Windows-focused Headroom development/review context. - Exact command / steps: Reviewed the safe update flow for locked and unlocked `_core.pyd` cases, including backup, pip invocation, import validation, and restore behavior. - Observed result: The update path avoids replacing a loaded native extension and provides an atomic restore path when an unlocked update fails validation. - Not tested: End-to-end package publication/install from PyPI as part of this body cleanup. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Additional Notes This body was normalized by a maintainer after approval so the governance parser reflects the already-reviewed PR state. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
5229c98228
|
deps: bump prometheus from 0.13.4 to 0.14.0 (#1518)
Bumps [prometheus](https://github.com/tikv/rust-prometheus) from 0.13.4 to 0.14.0. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/tikv/rust-prometheus/blob/master/CHANGELOG.md">prometheus's changelog</a>.</em></p> <blockquote> <h2>0.14.0</h2> <ul> <li> <p>API change: Use <code>AsRef<str></code> for owned label values (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/537">#537</a>)</p> </li> <li> <p>Improvement: Hashing improvements (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/532">#532</a>)</p> </li> <li> <p>Dependency upgrade: Update <code>hyper</code> to 1.6 (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/524">#524</a>)</p> </li> <li> <p>Dependency upgrade: Update <code>procfs</code> to 0.17 (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/543">#543</a>)</p> </li> <li> <p>Dependency upgrade: Update <code>protobuf</code> to 3.7.2 for RUSTSEC-2024-0437 (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/541">#541</a>)</p> </li> <li> <p>Dependency upgrade: Update <code>thiserror</code> to 2.0 (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/534">#534</a>)</p> </li> <li> <p>Internal change: Fix LSP and Clippy warnings (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/540">#540</a>)</p> </li> <li> <p>Internal change: Bump MSRV to 1.81 (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/539">#539</a>)</p> </li> <li> <p>Documentation: Fix <code>register_histogram_vec_with_registry</code> docstring (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/528">#528</a>)</p> </li> <li> <p>Documentation: Fix typos in static-metric docstrings (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/479">#479</a>)</p> </li> <li> <p>Documentation: Add missing <code>protobuf</code> feature to README list (<a href="https://redirect.github.com/tikv/rust-prometheus/issues/531">#531</a>)</p> </li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
e448d7ba4d
|
deps: bump thiserror from 1.0.69 to 2.0.18 (#1519)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.69 to 2.0.18. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/dtolnay/thiserror/releases">thiserror's releases</a>.</em></p> <blockquote> <h2>2.0.18</h2> <ul> <li>Make compatible with project-level <code>needless_lifetimes = "forbid"</code> (<a href="https://redirect.github.com/dtolnay/thiserror/issues/443">#443</a>, thanks <a href="https://github.com/LucaCappelletti94"><code>@LucaCappelletti94</code></a>)</li> </ul> <h2>2.0.17</h2> <ul> <li>Use differently named __private module per patch release (<a href="https://redirect.github.com/dtolnay/thiserror/issues/434">#434</a>)</li> </ul> <h2>2.0.16</h2> <ul> <li>Add to "no-std" crates.io category (<a href="https://redirect.github.com/dtolnay/thiserror/issues/429">#429</a>)</li> </ul> <h2>2.0.15</h2> <ul> <li>Prevent <code>Error::provide</code> API becoming unavailable from a future new compiler lint (<a href="https://redirect.github.com/dtolnay/thiserror/issues/427">#427</a>)</li> </ul> <h2>2.0.14</h2> <ul> <li>Allow build-script cleanup failure with NFSv3 output directory to be non-fatal (<a href="https://redirect.github.com/dtolnay/thiserror/issues/426">#426</a>)</li> </ul> <h2>2.0.13</h2> <ul> <li>Documentation improvements</li> </ul> <h2>2.0.12</h2> <ul> <li>Prevent elidable_lifetime_names pedantic clippy lint in generated impl (<a href="https://redirect.github.com/dtolnay/thiserror/issues/413">#413</a>)</li> </ul> <h2>2.0.11</h2> <ul> <li>Add feature gate to tests that use std (<a href="https://redirect.github.com/dtolnay/thiserror/issues/409">#409</a>, <a href="https://redirect.github.com/dtolnay/thiserror/issues/410">#410</a>, thanks <a href="https://github.com/Maytha8"><code>@Maytha8</code></a>)</li> </ul> <h2>2.0.10</h2> <ul> <li>Support errors containing a generic type parameter's associated type in a field (<a href="https://redirect.github.com/dtolnay/thiserror/issues/408">#408</a>)</li> </ul> <h2>2.0.9</h2> <ul> <li>Work around <code>missing_inline_in_public_items</code> clippy restriction being triggered in macro-generated code (<a href="https://redirect.github.com/dtolnay/thiserror/issues/404">#404</a>)</li> </ul> <h2>2.0.8</h2> <ul> <li>Improve support for macro-generated <code>derive(Error)</code> call sites (<a href="https://redirect.github.com/dtolnay/thiserror/issues/399">#399</a>)</li> </ul> <h2>2.0.7</h2> <ul> <li>Work around conflict with #[deny(clippy::allow_attributes)] (<a href="https://redirect.github.com/dtolnay/thiserror/issues/397">#397</a>, thanks <a href="https://github.com/zertosh"><code>@zertosh</code></a>)</li> </ul> <h2>2.0.6</h2> <ul> <li>Suppress deprecation warning on generated From impls (<a href="https://redirect.github.com/dtolnay/thiserror/issues/396">#396</a>)</li> </ul> <h2>2.0.5</h2> <ul> <li>Prevent deprecation warning on generated impl for deprecated type (<a href="https://redirect.github.com/dtolnay/thiserror/issues/394">#394</a>)</li> </ul> <h2>2.0.4</h2> <ul> <li>Eliminate needless_lifetimes clippy lint in generated <code>From</code> impls (<a href="https://redirect.github.com/dtolnay/thiserror/issues/391">#391</a>, thanks <a href="https://github.com/matt-phylum"><code>@matt-phylum</code></a>)</li> </ul> <h2>2.0.3</h2> <ul> <li>Support the same Path field being repeated in both Debug and Display representation in error message (<a href="https://redirect.github.com/dtolnay/thiserror/issues/383">#383</a>)</li> <li>Improve error message when a format trait used in error message is not implemented by some field (<a href="https://redirect.github.com/dtolnay/thiserror/issues/384">#384</a>)</li> </ul> <h2>2.0.2</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
98f7f1c2a3
|
deps: bump tower-http from 0.6.11 to 0.7.0 (#1520)
Bumps [tower-http](https://github.com/tower-rs/tower-http) from 0.6.11 to 0.7.0. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/tower-rs/tower-http/releases">tower-http's releases</a>.</em></p> <blockquote> <h2>tower-http-0.7.0</h2> <p><a href="https://github.com/tower-rs/tower-http/compare/tower-http-0.6.11...tower-http-0.7.0">Changes since 0.6.11</a></p> <h2>Added</h2> <ul> <li> <p><code>csrf</code>: add cross-site request forgery (CSRF) protection middleware, porting the cross-origin protection scheme introduced in Go 1.25 (<a href="https://redirect.github.com/tower-rs/tower-http/issues/699">#699</a>)</p> <pre lang="rust"><code>use tower::ServiceBuilder; use tower_http::csrf::CsrfLayer; <p>// Rejects cross-origin state-changing requests using <code>Sec-Fetch-Site</code>,<br /> // an <code>Origin</code> allow-list, and an <code>Origin</code>/<code>Host</code> fallback. No per-request<br /> // token state required.<br /> let layer = CsrfLayer::new().add_trusted_origin("<a href="https://example.com">https://example.com</a>")?;</p> <p>let service = ServiceBuilder::new().layer(layer).service_fn(handler);<br /> </code></pre></p> </li> <li> <p><code>timeout</code>: add <code>DeadlineBody</code> for non-resetting body timeouts, applied via the new <code>RequestBodyDeadlineLayer</code> and <code>ResponseBodyDeadlineLayer</code> (<a href="https://redirect.github.com/tower-rs/tower-http/issues/688">#688</a>)</p> <p>Unlike <code>TimeoutBody</code>, which resets its deadline on every frame, <code>DeadlineBody</code> caps the total time of a body transfer. A slow client trickling one byte at a time never trips an idle timeout but will trip a deadline.</p> <pre lang="rust"><code>use std::time::Duration; use tower::ServiceBuilder; use tower_http::timeout::RequestBodyDeadlineLayer; <p>// Abort the request body transfer after 30s total, regardless of how<br /> // frequently data arrives.<br /> let service = ServiceBuilder::new()<br /> .layer(RequestBodyDeadlineLayer::new(Duration::from_secs(30)))<br /> .service_fn(handler);<br /> </code></pre></p> </li> <li> <p><code>fs</code>: add strong <code>ETag</code> support to <code>ServeDir</code>, including <code>If-Match</code> and <code>If-None-Match</code> precondition handling per RFC 9110. <code>304 Not Modified</code> responses now carry the <code>ETag</code> and <code>Last-Modified</code> validators (<a href="https://redirect.github.com/tower-rs/tower-http/issues/691">#691</a>)</p> </li> <li> <p><code>fs</code>: add a <code>Backend</code> trait to make <code>ServeDir</code> work with non-filesystem sources (e.g. embedded assets or object storage). The default <code>TokioBackend</code> preserves existing behavior. Use <code>ServeDir::with_backend()</code> to plug in custom implementations (<a href="https://redirect.github.com/tower-rs/tower-http/issues/684">#684</a>)</p> <pre lang="rust"><code>use tower_http::services::fs::ServeDir; <p>// <code>MyBackend</code> implements <code>tower_http::services::fs::Backend</code>.<br /> // The default <code>ServeDir::new()</code> continues to use <code>TokioBackend</code> (local FS).<br /> let service = ServeDir::with_backend("assets", MyBackend::new());<br /> </code></pre></p> </li> <li> <p><code>fs</code>: add <code>html_as_default_extension</code> option to <code>ServeDir</code>, appending <code>.html</code> when the request path has no extension (<a href="https://redirect.github.com/tower-rs/tower-http/issues/519">#519</a>)</p> </li> <li> <p><code>fs</code>: add <code>redirect_path_prefix</code> option to <code>ServeDir</code>, prepending a prefix on trailing-slash redirects so the service can be mounted under a sub-path (<a href="https://redirect.github.com/tower-rs/tower-http/issues/486">#486</a>)</p> </li> <li> <p><code>validate-request</code>: add <code>ValidateRequestHeaderLayer::has_header_value()</code> to reject requests when a header does not have an expected value (<a href="https://redirect.github.com/tower-rs/tower-http/issues/360">#360</a>)</p> </li> <li> <p><code>body</code>: <code>UnsyncBoxBody::new()</code> constructor and <code>From<ServeFileSystemResponseBody></code> conversion to avoid double-boxing when combining <code>ServeDir</code> responses with other body types (<a href="https://redirect.github.com/tower-rs/tower-http/issues/537">#537</a>)</p> </li> <li> <p><code>limit</code>: implement <code>Default</code> for <code>limit::ResponseBody</code> when the wrapped body also implements <code>Default</code> (<a href="https://redirect.github.com/tower-rs/tower-http/issues/679">#679</a>)</p> </li> </ul> <h2>Changed</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
6c705b4066
|
deps: bump toml from 0.8.23 to 1.1.2+spec-1.1.0 (#1517)
Bumps [toml](https://github.com/toml-rs/toml) from 0.8.23 to 1.1.2+spec-1.1.0. <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
98842b847b
|
Extract loop callback failure policy (#1977)
## Description Extracts the event-loop callback failure classifier for a known WebSocket disconnect regression from `server.py` into `headroom.proxy.loop_callback_failure_policy`. The server keeps `_is_known_websocket_callback_failure` as a compatibility alias for the existing loop-health path. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `loop_callback_failure_policy.py` with constants for the known message and exception shape. - Replaced the inline server helper body with a compatibility alias to the extracted classifier. - Added direct classifier tests and ran the existing loop-health regression tests. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_loop_callback_failure_policy.py tests\test_proxy_loop_exception_health.py 5 passed in 4.89s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-27`. - Exact command / steps: ran new loop callback classifier tests, existing loop-health tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: classifier behavior and endpoint-level loop-health behavior remain covered; local lint/type/security checks pass. - Not tested: live WebSocket disconnect reproduction; this slice preserves the existing server alias. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
b5b59bcd77
|
Extract project name policy (#1974)
## Description Extracts project-name normalization for proxy attribution from `savings_tracker.py` into `headroom.proxy.project_name_policy`. `savings_tracker.sanitize_project_name` and `PROJECT_NAME_MAX_LENGTH` remain compatibility aliases for existing callers. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `project_name_policy.py` for project-name decoding, printable-character filtering, trimming, and length capping. - Kept `savings_tracker` compatibility aliases for existing imports and project-context callers. - Added focused policy tests plus re-export coverage. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_project_name_policy.py tests\test_proxy_project_savings.py 20 passed in 17.05s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-26`. - Exact command / steps: ran new project-name policy tests, existing project savings tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: project attribution/savings behavior remains covered and local lint/type/security checks pass. - Not tested: full proxy runtime; this slice preserves existing `savings_tracker` imports. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
2b09ecea76
|
refactor(proxy): isolate image compression policy (#1958)
## Description Extracts image-compression gating and tag stamping into a pure policy module while preserving the public `ImageCompressionDecision.decide` API used by handlers. This keeps the frozen decision value type separate from the canonical precedence rules it wraps. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.image_compression_policy` for pure image-compression precedence and tag stamping helpers. - Updated `ImageCompressionDecision.decide` and `ImageCompressionDecision.apply_to_tags` to delegate to the extracted policy. - Added direct tests for the extracted policy boundary. - Kept the LiteLLM callback compatibility shim required for repo-wide type checking on fresh branches. ## 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 python -m pytest tests/test_image_compression_policy.py tests/test_image_compression_decision.py tests/test_handler_outcome_tag_invariant.py tests/test_litellm_callback.py tests/test_compress_api.py::TestLiteLLMCallback -q 34 passed in 6.77s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local worktree based on `headroomlabs/main`. - Exact command / steps: ran focused image compression policy/decision tests, handler outcome tag invariant tests, LiteLLM callback tests, Ruff lint/format checks, mypy over `headroom`, and staged gitleaks scan. - Observed result: all local checks passed; staged secret scan found no leaks. - Not tested: full CI matrix and deployment flows; those are covered by GitHub Actions. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. GitHub reported existing Dependabot alerts on the default branch during push; this PR does not change dependencies, and the staged secret scan is clean. |
||
|
|
9db8a6bbf6
|
fix(proxy): handle ClientDisconnect in passthrough body reads (#2033)
## Description
Catch `starlette.requests.ClientDisconnect` when reading request bodies
in passthrough/forwarding handlers. Closes #2019
Without this, a client that disconnects mid-request causes an unhandled
`ClientDisconnect` to propagate through the entire middleware stack,
crashing the ASGI TaskGroup and contributing to proxy instability over
long sessions (memory growth, freeze, unresponsive to SIGTERM).
**Adversarial review uncovered 3 additional unprotected sites** in
`proxy_routes.py` — same pattern (body read before try/except). Now
fixed.
## Type of Change
- [x] Bug fix (non-breaking change which fixes an issue)
## Changes Made
**Proxy handlers** (6 sites, first commit):
- openai `handle_passthrough`: wrap `await request.body()` in try/except
ClientDisconnect (main crash site)
- openai `_handle_streaming_passthrough`: same protection
- anthropic batch passthrough: same protection
- batch `_google_batch_passthrough`: same protection
- batch `handle_google_batch_passthrough`: same protection
- bedrock fallback-forward path: early-return on ClientDisconnect
instead of attempting verbatim forward
**Proxy routes** (3 sites, second commit — found by adversarial design
scan):
- `_handle_chatgpt_model_metadata` (proxy_routes.py:398)
- `_handle_chatgpt_codex_images` (proxy_routes.py:438)
- `openai_responses_sub` nested handler (proxy_routes.py:597)
All nine sites return HTTP 204 on disconnect to allow the request to
terminate cleanly.
## Testing
- [x] **Existing tests**: 34/34 pass in `test_proxy_handler_helpers.py`
- [x] **Unit tests**: 2 new tests — passthrough + streaming passthrough
disconnect
- [x] **Adversarial concurrency**: 50 threads × 10 iterations = 500
concurrent disconnect requests — zero crashes, all return 204
- [x] **Adversarial edge cases**: minimal request state, regression
check (normal request path unaffected)
- [x] **PBT (Hypothesis)**: 250 random method/path combinations, 3
properties verified:
- All disconnect requests return 204
- ClientDisconnect never leaks out of handler
- Response is always valid HTTP 2xx
```text
# Unit tests
tests/test_proxy_handler_helpers.py::test_handle_passthrough_client_disconnect PASSED
tests/test_proxy_handler_helpers.py::test_handle_streaming_passthrough_client_disconnect PASSED
# PBT (3 properties × 100-250 examples each)
/tmp/pbt_client_disconnect.py::test_disconnect_always_returns_204 PASSED
/tmp/pbt_client_disconnect.py::test_disconnect_does_not_crash_asgi PASSED
/tmp/pbt_client_disconnect.py::test_response_is_valid_http PASSED
# Adversarial
/tmp/adversarial_client_disconnect.py → 500 concurrent requests: 0 errors, all 204
```
- [x] `ruff check` and `ruff format --check` pass on all changed files
## Real Behavior Proof
- Environment: Linux, Python 3.12, headroom main @
|
||
|
|
fd0d29c92d
|
fix(packaging): guard torch extras on intel macos (#2011)
## Description Closes #1931 Guard the `ml` and `voice` `torch` optional dependencies on macOS x86_64 so `headroom-ai[all]` remains resolvable on Intel Macs where PyTorch does not publish compatible wheels for this version floor. The lockfile metadata is updated with the same markers. ## Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [ ] Refactoring - [ ] Performance improvement - [ ] Test update - [ ] Other ## Changes Made - Added macOS x86_64 environment markers to `torch` in the `ml` and `voice` extras. - Updated `uv.lock` optional dependency metadata to match the guarded extras. - Added a packaging regression test that checks `[all]` keeps `ml` and `voice` while guarding `torch` on macOS x86_64. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check`) - [x] Formatting verified (`ruff format --check`) - [ ] Manual testing performed ### Test Output ```text $ python3 -m pytest tests/test_optional_dependencies.py -q collected 1 item tests/test_optional_dependencies.py . [100%] ============================== 1 passed in 0.26s =============================== $ .venv/bin/ruff check tests/test_optional_dependencies.py All checks passed! $ .venv/bin/ruff format --check tests/test_optional_dependencies.py pyproject.toml 1 file already formatted ``` ## Test verification (RED -> GREEN) RED, with the `torch` markers temporarily removed from `pyproject.toml`: ```text tests/test_optional_dependencies.py F [100%] FAILED tests/test_optional_dependencies.py::test_all_extra_does_not_require_torch_on_macos_x86_64 E assert False ``` GREEN, with this patch applied: ```text tests/test_optional_dependencies.py . [100%] ============================== 1 passed in 0.26s =============================== ``` ## Real Behavior Proof - Environment: Linux, Python 3.12.3, pytest 9.1.1, ruff 0.14.14. - Exact command / steps: Removed the environment markers from `torch`, ran the new packaging test, restored the markers, and reran the test plus targeted ruff checks. - Observed result: The test fails without the macOS x86_64 guard and passes once the `ml` and `voice` `torch` requirements are guarded. - Not tested: Full `uv run pytest`, full-project `uv run ruff check .`, full-project `uv run ruff format --check .`, and `uv run mypy headroom` were not run locally for this targeted packaging change. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have added tests that prove my fix is effective - [x] New and existing targeted tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules ## Screenshots (if applicable) N/A ## Additional Notes No new dependency is added; this only narrows when the existing `torch` optional dependency is selected. |
||
|
|
f536aa0801
|
fix(wrap): keep Claude context-tool setup explicit (#1999)
## Description `headroom wrap claude` currently installs RTK's global Claude hook and instruction imports on a flag-free launch, even though the wrapped session already routes through Headroom's proxy. The wrapper now requires an explicit Claude context-tool opt-in before it runs the existing RTK or lean-ctx setup path. Existing negative flags remain accepted, and other wrapped agents keep their current behavior. Closes #1915 ## 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 - Made Claude context-tool installation explicit instead of running it on every default wrap. - Preserved the existing RTK and lean-ctx installers behind the positive opt-in. - Kept `--no-context-tool` and `--no-rtk` compatible and left other agent wrappers unchanged. - Added focused command-parser coverage for default, opt-in, selector, and negative-space behavior. - Documented the changed default and opt-in command in `CHANGELOG.md`. ## Testing - [x] Unit tests pass (`uv run --no-project pytest tests/test_cli/test_wrap_helpers.py -q`) - [x] Linting passes (`uv run --no-project ruff check headroom/cli/wrap.py tests/test_cli/test_wrap_helpers.py`) - [ ] Type checking passes (`uv run mypy headroom`) - [x] New tests added for new functionality when applicable - [ ] Manual testing performed ### Test Output ```text uv run --no-project pytest tests/test_cli/test_wrap_helpers.py -q 65 passed uv run --no-project ruff check headroom/cli/wrap.py tests/test_cli/test_wrap_helpers.py All checks passed uv run --no-project ruff format --check headroom/cli/wrap.py tests/test_cli/test_wrap_helpers.py 2 files already formatted ``` ## Real Behavior Proof - Environment: isolated HOME on Linux or macOS, Python 3.12+, Claude CLI available. - Exact command / steps: run `headroom wrap claude --prepare-only` without a context-tool flag, inspect the isolated Claude config, then repeat with the explicit context-tool opt-in. - Observed result: the focused Click harness now proves the default run creates no RTK setup calls, the explicit opt-in performs the existing RTK setup, `--no-context-tool` still wins if both flags are present, and Copilot still keeps its default context-tool behavior. - Not tested: a live `headroom wrap claude` run against a real Claude installation and a real RTK or lean-ctx hook write on this host. - Scope: Claude context-tool activation and global configuration artifacts. ## 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 - [ ] I have commented my code, particularly in hard-to-understand areas - [x] 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 have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes The exact project-bound `uv sync --extra dev` flow was blocked on this host by a `rustup.exe` access error, so the focused checks used `uv run --no-project` against the existing environment. This PR does not change RTK installation internals, proxy compression, or context-tool defaults for other agents. |
||
|
|
e92c253977
|
refactor(proxy): extract ccr session tracker (#2003)
## Description Extracts the sticky CCR session tracker from `headroom.proxy.helpers` into a focused state module. `helpers.SessionCcrTracker` remains as an env-aware compatibility wrapper so existing CCR tool injection and singleton call sites keep the same API. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.ccr_session_tracker.SessionCcrTracker` as the pure bounded LRU CCR state holder. - Replaced the in-helper CCR tracker implementation with a small env-aware wrapper. - Added direct tracker tests for unknown sessions, monotonic done state, first-write golden bytes, provider isolation, LRU eviction, reset, and input validation. ## 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 python -m pytest tests/test_ccr_session_tracker.py tests/test_ccr_tool_always_on.py tests/test_corrupt_golden_bytes_recovery.py tests/test_issue_728_empty_tools_injection.py 35 passed in 0.69s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13 - Exact command / steps: Ran direct CCR tracker tests, CCR always-on tests, corrupt golden byte recovery tests, empty tools injection regression tests, full ruff, format check, mypy, and staged gitleaks scan. - Observed result: Existing sticky CCR tool behavior and recovery behavior remain green while the CCR session state domain is directly covered. - Not tested: Full repository pytest suite locally; CI covers the broader matrix. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. The default-branch Dependabot alerts reported during push are pre-existing and unrelated to this PR. |
||
|
|
4e19bcf6ce
|
test(memory): skip decorators on offline model misses (#2020)
## Description Current `main` already has the shared `external_model_skip_reason` helper and pytest hooks for transient/offline model dependency failures. This follow-up applies the same classifier to the async memory integration test decorators in `test_core_operations.py` and `test_easy.py`, so decorated tests also skip offline Hugging Face cache-miss errors instead of only `httpx.ReadTimeout`. Supersedes #1017 with a clean branch based on current `main`. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) ## Changes Made - Updated `network_timeout_handler` in `tests/test_memory/test_core_operations.py` to call `external_model_skip_reason` and re-raise unrelated exceptions. - Updated `network_timeout_handler` in `tests/test_memory/test_easy.py` the same way. - Removed now-unnecessary direct `httpx` imports from those files. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check`) - [x] Type checking passes (`mypy` via commit hook) - [x] New tests added for new functionality ### Test Output ```text $ python -m pytest tests/test_memory/test_skip_helpers.py -q 4 passed in 0.12s $ python -m ruff check tests/test_memory/test_core_operations.py tests/test_memory/test_easy.py tests/test_memory/test_skip_helpers.py All checks passed! $ python -m ruff format --check tests/test_memory/test_core_operations.py tests/test_memory/test_easy.py tests/test_memory/test_skip_helpers.py 3 files already formatted $ git commit -m "test(memory): skip decorators on offline model misses" Sync plugin versions.....................................................Passed check for merge conflicts................................................Passed ruff.....................................................................Passed ruff-format..............................................................Passed mypy.....................................................................Passed ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local `C:\git\headroom` checkout. - Exact command / steps: ran `python -m pytest tests/test_memory/test_skip_helpers.py -q` against the skip classifier used by these decorators. - Observed result: `4 passed`, covering `httpx.ReadTimeout`, `LocalEntryNotFoundError`, offline Hugging Face `OSError`, and unrelated errors. - Not tested: live memory integration against an intentionally missing Hugging Face cache. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review |
||
|
|
12aa2cbf6c
|
fix(kompress): surface model-not-ready state via logs and health endpoint (#2034)
## Description
Kompress model download failures (HuggingFace unreachable, corporate
firewall, SSL errors) previously caused **silent 0% compression** — the
model isn't loaded, `is_ready()` returns `False`, and the proxy passes
through with no warning, no health indicator, nothing. Operators cannot
detect degraded operation without manually comparing
`x-headroom-tokens-before` vs `x-headroom-tokens-after` headers.
Closes #2029
## Type of Change
- [x] Bug fix (non-breaking change which fixes an issue)
## Changes Made
Three surfaces where silent failure is now visible:
- **Request-time (hot path)**: `ContentRouter.compress_text()` now logs
a WARNING once per router instance when `is_ready()` is False and the
model is not cached. Rate-limited to one log per session to avoid spam.
- **Startup (eager preload)**: `ContentRouter.eager_load_compressors()`
now logs WARNING (was INFO) when `KompressModelNotCached` is raised,
with actionable guidance ("Check HuggingFace connectivity or
pre-download with headroom-ai[ml]").
- **Health endpoint**: `/health` response now includes a `kompress`
component with the standard `enabled`/`ready`/`status`/`backend` fields.
Kompress is treated as **optional** in the aggregate readiness check — a
cold model cache does NOT degrade the overall proxy health status
(matching the semantics of `cache` and `rate_limiter` which report
`ready=True` when disabled).
## Testing
- [x] **Existing tests**: 13/13 pass in
`test_kompress_preload_deferral.py` + `test_proxy_disable_kompress.py` +
`test_proxy_ccr.py` + `test_proxy_debug_endpoints.py`
- [x] **Adversarial**: 5 endpoint-level tests verify `/health`,
`/healthz`, `/livez`, `/readyz` all return 200 even when kompress model
is not cached
- [x] **PBT (Hypothesis)**: 3 properties × 136 random combinations
confirm aggregate readiness ignores kompress, `_component_health()`
invariants hold, and kompress is never the sole cause of `unhealthy`
- [x] **Lint**: `ruff check` and `ruff format --check` pass on all
changed files
```text
$ uv run pytest tests/test_kompress_preload_deferral.py tests/test_proxy_disable_kompress.py \
tests/test_proxy_ccr.py::TestCCRIntegration::test_health_endpoint \
tests/test_proxy_debug_endpoints.py::test_existing_health_routes_unchanged -q
............. [100%]
13 passed in 2.90s
$ uv run pytest /tmp/adversarial_kompress_health.py -q
..... [100%]
5 passed in 9.60s
# Includes: /healthz, /livez, /readyz all 200; disable_kompress=True → status=disabled
$ uv run pytest /tmp/pbt_kompress_health.py -q
... [100%]
3 passed in 0.56s
# Hypothesis: 128 aggregate combos + 4 invariant combos + 4 isolation combos
```
## Real Behavior Proof
- Environment: Linux, Python 3.12, headroom main @
|
||
|
|
d8783ab89b
|
fix(cache/semantic): key entries by context hash, not query text (#2022)
## Description
`SemanticCache` (`headroom/cache/semantic.py`) derives each entry's key
from the **query text
only** — where `query` is just the trailing user message — and its
exact-match lookup returns
the slot without checking the stored entry's `messages_hash`:
```python
# put()
key = self._generate_key(query) # sha256(query)[:16]
self._cache[key] = entry
if messages_hash:
self._hash_index[messages_hash] = key
# get() — exact-match branch
key = self._hash_index.get(messages_hash)
if key and key in self._cache:
entry = self._cache[key]
...
return entry # never checks entry.messages_hash
```
So two requests that share a trailing user message but differ in earlier
context map to the
**same** key. The second `put` overwrites the first, and the first
request's `messages_hash`
still points at that (now overwritten) slot — so it is served the
**other conversation's**
cached response.
Trailing messages like `"continue"`, `"yes"`, `"fix it"`, `"run the
tests"` are extremely
common in agentic/coding sessions, so this collides constantly. It's
independent of the
proxy-level `_compute_key` fix (that's about what goes *into*
`messages_hash`; here the entry
is stored under a query-only key regardless of how good the hash is).
This `SemanticCache` is
the one used by the SDK client's `enable_semantic_cache` path.
Concretely:
1. `put("run the tests", A, messages_hash=HA)` → key `K = sha256("run
the tests")`; `_cache[K]=A`.
2. `put("run the tests", B, messages_hash=HB)` → same `K`; `_cache[K]`
overwritten with `B`.
3. `get("run the tests", HA)` → `_hash_index[HA]=K`, `K in _cache` →
returns **B**.
Closes: no issue filed — found while auditing the cache key derivation.
## Fix
1. Key entries by the full-context `messages_hash` when present, falling
back to the query hash
only when no hash is supplied:
```python
key = messages_hash or self._generate_key(query)
```
2. Defensively verify `entry.messages_hash == messages_hash` in the
exact-match branch of `get`,
so any residual stale mapping becomes a miss rather than wrong data.
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- `headroom/cache/semantic.py`: key `put` entries by `messages_hash`
when present; verify `entry.messages_hash` in the `get` exact-match
branch.
- `tests/test_cache/test_semantic.py`: add
`test_same_query_different_context_does_not_collide` and
`test_exact_match_verifies_messages_hash`.
## Testing
- [x] New regression tests added (`tests/test_cache/test_semantic.py`)
- [x] Linting/formatting clean — run with the CI-pinned `ruff==0.15.17`
- [ ] Full `pytest` deferred to CI (local-OOM reason below).
```text
$ uvx ruff@0.15.17 check headroom/cache/semantic.py tests/test_cache/test_semantic.py
All checks passed!
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.10, headroom from this branch.
Importing `headroom` pulls in the torch/transformers stack and a full
`pytest` gets OOM-killed on this box, so I verified the `put`/`get`
logic with a dependency-free script and left the full pytest to CI.
- Exact command / steps: stored responses A and B under the same query
`"run the tests"` with different `messages_hash`, then read each hash
back — through both the old (query-keyed) and new (hash-keyed) logic.
- Observed result: the old logic serves B's response to request A; the
new logic isolates them:
```text
OLD: A->RESPONSE_B B->RESPONSE_B
NEW: A->RESPONSE_A B->RESPONSE_B
SEMANTIC CACHE COLLISION FIX VERIFIED (OLD served B to A; NEW isolates)
```
- Not tested: the full SDK `HeadroomClient` round-trip with
`enable_semantic_cache=True` (needs the heavy stack). The fix is
confined to `SemanticCache.put`/`get` and the new tests drive them
directly. Full local `pytest` deferred to CI (OOM, per above).
## 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
- [ ] New and existing unit tests pass locally with my changes — ran
lint + a standalone logic check; full pytest deferred to CI (local OOM,
disclosed above)
- [x] I have updated the CHANGELOG.md if applicable
## Additional Notes
- Small, contained fix — the key derivation plus a verification guard,
no new dependencies.
- @JerrettDavis tagging you — this one can serve one conversation's
cached response to another when the last message matches, so it seemed
worth surfacing. Thanks!
|
||
|
|
f8431240b9
|
Extract tool schema savings policy (#1971)
## Description Extracts the pure tool-schema savings attribution logic from `server.py` into `headroom.proxy.tool_schema_savings_policy`. The server keeps the `_tool_schema_saved_from_tags` compatibility alias used by the existing stats payload path. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `tool_schema_savings_policy.py` with stable savings tag names and pure summation behavior. - Replaced the inline `server.py` helper body with a compatibility alias to the extracted policy. - Added direct tests for valid tag summing, invalid values, non-mapping input, and stable tag names. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_tool_schema_savings_policy.py 4 passed in 0.14s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-24`. - Exact command / steps: ran focused tool-schema savings policy tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: pure policy behavior is directly covered and local lint/type/security checks pass. - Not tested: full proxy runtime; this slice only moves pure stats attribution logic while preserving the server alias. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
a617455f02
|
fix(proxy): preserve chatgpt responses streaming (#2012)
## Description Closes #1956 Keep ChatGPT OAuth `/v1/responses` requests streaming when CCR retrieve tools are present. The buffered `stream:false` conversion is still used for regular OpenAI Responses CCR requests, but ChatGPT Codex routing now bypasses that conversion so the upstream receives the streaming request shape it expects. ## Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update - [ ] Refactoring - [ ] Performance improvement - [ ] Test update - [ ] Other ## Changes Made - Extracted the OpenAI Responses CCR stream-buffering decision into a small helper. - Excluded ChatGPT OAuth/Codex-routed requests from the buffered `stream:false` path. - Added tests proving regular OpenAI CCR still buffers while ChatGPT OAuth CCR remains streaming. ## Testing - [x] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check`) - [x] Formatting verified (`ruff format --check`) - [ ] Manual testing performed ### Test Output ```text $ python3 -m pytest tests/test_proxy_openai_responses_stream_ccr.py -q collected 3 items tests/test_proxy_openai_responses_stream_ccr.py ... [100%] ============================== 3 passed in 0.59s =============================== $ .venv/bin/ruff check headroom/proxy/handlers/openai.py tests/test_proxy_openai_responses_stream_ccr.py All checks passed! $ .venv/bin/ruff format --check headroom/proxy/handlers/openai.py tests/test_proxy_openai_responses_stream_ccr.py 2 files already formatted ``` ## Test verification (RED -> GREEN) RED, with the ChatGPT OAuth guard temporarily removed from the buffering decision: ```text tests/test_proxy_openai_responses_stream_ccr.py .F. [100%] FAILED tests/test_proxy_openai_responses_stream_ccr.py::test_responses_ccr_keeps_chatgpt_oauth_requests_streaming E AssertionError: assert not True E + where True = _should_buffer(tools=[{'type': 'function', 'name': 'headroom_retrieve'}], is_chatgpt_auth=True) ``` GREEN, with this patch applied: ```text tests/test_proxy_openai_responses_stream_ccr.py ... [100%] ============================== 3 passed in 0.59s =============================== ``` ## Real Behavior Proof - Environment: Linux, Python 3.12.3, pytest 9.1.1, ruff 0.14.14. - Exact command / steps: Removed the `not is_chatgpt_auth` guard from the CCR buffering decision, ran the targeted tests, restored the guard, and reran the tests plus targeted ruff checks. - Observed result: The ChatGPT OAuth streaming regression test fails without the guard and passes with the guard, while regular OpenAI CCR buffering remains covered. - Not tested: Full `uv run pytest`, full-project `uv run ruff check .`, full-project `uv run ruff format --check .`, and `uv run mypy headroom` were not run locally; `uv run --extra dev ruff` attempted to build the Rust extension in this worktree, so targeted checks used the existing `.venv/bin/ruff`. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have added tests that prove my fix is effective - [x] New and existing targeted tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules ## Screenshots (if applicable) N/A ## Additional Notes The existing buffered CCR path is preserved for non-ChatGPT OpenAI Responses requests. |
||
|
|
70b98b6485
|
Extract Python forwarder mode policy (#1987)
## Description Extracts Python-forwarder mode resolution from `helpers.py` into `headroom.proxy.python_forwarder_mode_policy`. The forwarding helpers still read `HEADROOM_PROXY_PYTHON_FORWARDER_MODE` at request time, while the allowed values/default/error contract is now pure and directly tested. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `python_forwarder_mode_policy.py` with the allowed mode type, env name/default, and resolver. - Kept `helpers.get_python_forwarder_mode` as the request-time env reader and compatibility entry point. - Added direct policy tests for defaults, accepted values, normalization, and invalid mode rejection. ## 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 python -m pytest tests\test_python_forwarder_mode_policy.py tests\test_proxy_byte_faithful_forwarding.py 41 passed in 4.00s python -m ruff check . All checks passed! python -m ruff format --check . 1069 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 410 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-34`. - Exact command / steps: ran new Python-forwarder mode policy tests, existing byte-faithful forwarding tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: forwarder mode behavior and byte-faithful forwarding tests remain covered; local lint/type/security checks pass. - Not tested: live proxy forwarding; existing helper entry point remains intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
0f846e5a8f
|
refactor(proxy): extract tool injection config (#2010)
## Description
Extracts memory tool-injection operator config parsing from
`headroom.proxy.helpers` into a focused config policy module. Existing
helper functions and imports remain available while the environment
parsing is now directly testable.
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
- [x] Code refactoring (no functional changes)
## Changes Made
- Added `headroom.proxy.tool_injection_config` for
`HEADROOM_TOOL_INJECTION_STICKY` and
`HEADROOM_TOOL_TRACKER_MAX_SESSIONS` parsing.
- Updated `helpers.get_tool_injection_sticky_mode` and
`helpers.get_tool_tracker_max_sessions` to delegate to the config module
while preserving existing import paths.
- Added direct tests for defaults, valid values, invalid values, and
helper wrapper compatibility.
## 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
python -m pytest tests/test_tool_injection_config.py tests/test_memory_tool_session_sticky.py tests/test_issue_728_empty_tools_injection.py
46 passed in 0.53s
python -m ruff check .
All checks passed!
python -m ruff format --check .
1078 files already formatted
python -m mypy headroom --ignore-missing-imports
Success: no issues found in 415 source files
gitleaks protect --staged --no-banner --redact
no leaks found
```
## Real Behavior Proof
- Environment: Windows, Python 3.13.13, clean worktree from
`headroomlabs/main` at `
|
||
|
|
b3a559ba56
|
fix(savings): cap ledger retention at 30 days (#1985)
## Description The durable savings ledger (`headroom savings`) retained up to 365 days of history with an unbounded-sounding "All time" window. Long-lived installs accumulate an ever-growing `~/.headroom/savings_events.jsonl`, and `--days` had no upper bound so a caller could request an arbitrarily large lookback. This caps retention at 30 days everywhere it's read, shrinks the compaction threshold to match, and renames the "All time" window to reflect what it actually is now: `Last 30 days`. ## Type of Change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which 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 - `headroom/savings_ledger.py`: `DEFAULT_RETENTION_DAYS` 365 → 30; add `MAX_RETENTION_DAYS = 30` and hard-clamp the lookback inside `aggregate_savings` so no caller (CLI or programmatic) can read back further than 30 days, regardless of the `retention_days` argument passed in. - `headroom/savings_ledger.py`: report window `all_time` → `last_30_days` (the bucket is exactly 30-day-bounded now, so it doubles as the lifetime view too). `_COMPACT_SIZE_BYTES` 8 MiB → 1 MiB, since a 30-day-bounded ledger should never need to grow large. - `headroom/cli/savings.py`: `--days` is now `click.IntRange(min=1, max=30)` (was unbounded); help text states the max. Window label `"All time"` → `"Last 30 days"`, and the label column width bumped 11 → 12 so the longer label stays aligned with the other rows' progress bars. - `tests/test_savings_ledger.py`: updated window-label assertions; added a hard-cap regression test (`retention_days=365` passed explicitly still excludes a 60-day-old event) and a `--days` range-rejection test (31/60/365 all rejected). ## Testing - [x] Unit tests pass (`pytest`) - [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 $ ruff check headroom/savings_ledger.py headroom/cli/savings.py tests/test_savings_ledger.py All checks passed! $ ruff format --check headroom/savings_ledger.py headroom/cli/savings.py tests/test_savings_ledger.py 3 files already formatted $ mypy headroom --ignore-missing-imports Success: no issues found in 409 source files $ pytest tests/test_savings_ledger.py -q ............ss.... [100%] 16 passed, 2 skipped in 6.11s ``` (ruff `0.15.17`, mypy `1.20.2` — pinned to match `.github/workflows/ci.yml`'s `lint` job. Full multi-shard suite left to CI; ran the full touched-module suite locally.) ## Real Behavior Proof - Environment: macOS (Darwin 25.5.0), Python 3.13.14, local `uv` venv; branch built and installed via `uv tool install --force`. - Exact command / steps: ran `headroom savings` against a ledger holding multiple models' events (claude-opus-4-8, claude-sonnet-5, claude-haiku-4-5) recorded across the retention window, then ran `headroom savings --days 60` to exercise the new upper bound. - Observed result: all three windows (Today / Last 7 days / Last 30 days) populate and are each bounded to at most 30 days; cost-avoided breaks down per model; `--days 60` is rejected by the new `1..30` range instead of silently accepted. - Not tested: Windows/macOS native-wrapper e2e jobs — left to CI. ```text $ headroom savings Today █████░░░░░░░░░░░ 33.8% saved 8,702,348 / 25,781,326 tokens $25.5830 Last 7 days ██████░░░░░░░░░░ 36.3% saved 11,289,737 / 31,072,254 tokens $34.8287 Last 30 days ██████░░░░░░░░░░ 38.2% saved 14,449,516 / 37,821,634 tokens $48.5385 Cost avoided per model: claude-opus-4-8 $33.0494 claude-sonnet-5 $15.2989 claude-haiku-4-5-20251001 $0.1902 $ headroom savings --days 60 Usage: headroom savings [OPTIONS] Try 'headroom savings --help' for help. Error: Invalid value for '--days': 60 is not in the range 1<=x<=30. ``` - Not tested: Windows/macOS native-wrapper e2e jobs — left to CI. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [x] 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 have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A — CLI text output only, see Real Behavior Proof above. |
||
|
|
82af5cdfe2
|
refactor(proxy): isolate proxy mode policy (#1965)
## Description Extracts proxy mode normalization into a pure `proxy_mode_policy` module. `modes.py` keeps the existing public API and logging, while alias/default/unknown-mode decisions are now represented by a deterministic value object with direct tests. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.proxy_mode_policy` with canonical mode constants, alias mapping, `ProxyModeDecision`, and pure normalization helpers. - Updated `headroom.proxy.modes` to delegate normalization decisions while preserving existing constants, predicates, fallback behavior, and logging. - Added direct policy tests for canonical modes, legacy aliases, blank values, unknown values, and value-only normalization. - Included the current LiteLLM callback signature compatibility shim required for repo-wide mypy on main-based slices. ## 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 python -m pytest tests/test_proxy_mode_policy.py tests/test_proxy_modes.py tests/test_litellm_callback.py -q 17 passed in 7.84s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, clean worktree based on `headroomlabs/main`. - Exact command / steps: targeted pytest, ruff, format check, repo-wide mypy, staged gitleaks scan. - Observed result: proxy mode policy/modes/callback tests pass; static checks pass; no staged secrets detected. - Not tested: live proxy run; this slice preserves existing public mode helpers and only moves pure normalization policy. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal architecture slice. PR-specific GHAS checks will be monitored after opening. |
||
|
|
27ddde1f5e
|
fix(transforms/code): coerce language aliases instead of raising (#1975)
## Description
`CodeAwareCompressor.compress()` picks the language for AST-based
compression like this
(`headroom/transforms/code_compressor.py`):
```python
if language:
detected_lang = CodeLanguage(language.lower()) # <-- raises on anything not an exact enum value
confidence = 1.0
elif self.config.language_hint:
detected_lang = CodeLanguage(self.config.language_hint.lower())
confidence = 1.0
else:
detected_lang, confidence = detect_language(code)
```
`CodeLanguage` only accepts
`python`/`javascript`/`typescript`/`go`/`rust`/`java`/`c`/`cpp`/`perl`.
The very common markdown fence tags and hints — `js`, `ts`, `py`, `jsx`,
`tsx`, `node`, `rs`,
`c++` — are **not** enum values, so `CodeLanguage("js")` raises
`ValueError`. That construction
is *above* the method's own `try/except`, so:
- **Direct callers** — `CodeAwareCompressor().compress(code,
language="js")` and the module-level
`compress_code(code, language="js")` — crash with an uncaught
`ValueError`.
- **In the router (mixed content):** `split_into_sections` extracts the
raw fence tag
(`_CODE_FENCE_PATTERN` captures `\w*`, e.g. `js`) into
`ContentSection.language`, and that string
is passed straight into `compress(...)`. The `ValueError` is swallowed
by the outer `try/except`
in the strategy dispatch, so a ` ```js ` / ` ```ts ` / ` ```py ` block
silently **skips
code-aware compression** even when `enable_code_aware=True`, falling
back to the generic path.
So the three most common web/scripting languages, written with their
usual fence tags, never get
the structure-aware compressor.
Closes: no issue filed — found while auditing the code-compression
language path.
## Fix
Add a `coerce_language()` helper that maps common aliases/fence tags to
the canonical
`CodeLanguage` and returns `CodeLanguage.UNKNOWN` (never raises) for
anything unrecognized.
`compress()` now coerces the hint and, when the result is `UNKNOWN`,
falls back to
content-based `detect_language(code)` instead of constructing the enum
directly:
```python
if language:
detected_lang = coerce_language(language)
if detected_lang == CodeLanguage.UNKNOWN:
detected_lang, confidence = detect_language(code)
else:
confidence = 1.0
```
## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
## Changes Made
- `headroom/transforms/code_compressor.py`: add `_LANGUAGE_ALIASES` and
`coerce_language()`; use them in `compress()` for both the `language`
argument and `config.language_hint`, with a content-detection fallback
on `UNKNOWN`.
- `tests/test_code_compressor_language_alias.py`: cover alias mapping,
canonical passthrough, case/whitespace handling, unknown-returns-UNKNOWN
(no `ValueError`), and that `compress(language="js")` no longer raises.
## Testing
- [x] New regression tests added
(`tests/test_code_compressor_language_alias.py`)
- [x] Linting passes (`ruff check`) and formatting is clean (`ruff
format --check`)
- [ ] Full `pytest` deferred to CI (local-OOM reason below).
```text
$ uv run ruff check headroom/transforms/code_compressor.py tests/test_code_compressor_language_alias.py
All checks passed!
```
## Real Behavior Proof
- Environment: Windows 11, Python 3.10, headroom from this branch.
Importing `headroom` pulls in the torch/transformers stack and a full
`pytest` gets OOM-killed on this box, so I verified the coercion logic
with a dependency-free script (replicating the enum + helper) and left
the full pytest to CI.
- Exact command / steps: ran the common aliases and the canonical values
through both the old `CodeLanguage(value.lower())` construction and the
new `coerce_language()`.
- Observed result: the old construction raises `ValueError` on every
alias (the crash / silent-skip); the new helper maps them and never
raises:
```text
OK alias 'js': old raised ValueError -> new maps to javascript
OK alias 'ts': old raised ValueError -> new maps to typescript
OK alias 'py': old raised ValueError -> new maps to python
OK alias 'jsx': old raised ValueError -> new maps to javascript
OK alias 'node': old raised ValueError -> new maps to javascript
OK canonical values pass through
OK case-insensitive + trimmed
OK unknown -> UNKNOWN (no ValueError)
LANGUAGE COERCION VERIFIED
```
- Not tested: running a full mixed-content document with ` ```js `
fences through a booted compression pipeline (needs the heavy stack).
The unit tests exercise the coercion directly and the
`compress(language="js")` entry point. Full local `pytest` deferred to
CI (OOM, per above).
## 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
- [ ] New and existing unit tests pass locally with my changes — ran
lint + a standalone logic check; full pytest deferred to CI (local OOM,
disclosed above)
- [x] I have updated the CHANGELOG.md if applicable
## Additional Notes
- No new dependencies; a small lookup table plus a helper and a
call-site change.
- @JerrettDavis tagging you — this one silently disables code-aware
compression for the most common fence tags (`js`/`ts`/`py`), so it may
be worth a look when you have a moment.
|
||
|
|
69fd2189a3
|
Extract request limit policy (#1982)
## Description Extracts request/stream limit validation from `helpers.py` into `headroom.proxy.request_limit_policy`. The helpers still read environment variables at request time, but validation of SSE event size and body-too-large status values is now pure and directly tested. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `request_limit_policy.py` for resolving SSE event max bytes and body-too-large HTTP status values. - Kept `helpers.get_sse_event_max_bytes` and `helpers.get_body_too_large_status` reading env vars and delegating to the pure policy. - Added direct tests for defaults, valid override values, and invalid values. - Carried forward the LiteLLM callback compatibility shim needed for current mypy on `main`. ## 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 python -m pytest tests\test_request_limit_policy.py 10 passed in 0.17s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, branch `jd/architecture-slice-31`. - Exact command / steps: ran focused request-limit policy tests, ruff, ruff format check, mypy, and staged gitleaks scan. - Observed result: limit validation behavior is directly covered and local lint/type/security checks pass. - Not tested: live proxy request rejection; existing helper entry points remain intact. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are N/A for this internal architecture-only refactor. The push reported existing default-branch Dependabot alerts; no staged secret leaks were found for this PR. |
||
|
|
094a53c047
|
refactor(proxy): isolate output effort policy (#1961)
## Description Extracts provider-neutral output effort decisions into a pure `output_effort_policy` module. `output_shaper` still owns request mutation and labels, while the rank comparisons, legacy thinking clamp, and OpenAI text verbosity eligibility now live behind small deterministic functions. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.output_effort_policy` for effort lowering, legacy thinking budget clamping, and OpenAI text verbosity decisions. - Updated `output_shaper` to delegate those pure decisions while preserving existing labels and request mutation behavior. - Added focused policy tests for effort rank transitions, thinking clamp boundaries, and verbosity creation/lowering. - Included the current LiteLLM callback signature compatibility shim required for repo-wide mypy on main-based slices. ## 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 python -m pytest tests/test_output_effort_policy.py tests/test_output_shaper.py tests/test_litellm_callback.py -q 56 passed in 6.34s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, clean worktree based on `headroomlabs/main`. - Exact command / steps: targeted pytest, ruff, format check, repo-wide mypy, staged gitleaks scan. - Observed result: output effort policy/shaper/callback tests pass; static checks pass; no staged secrets detected. - Not tested: live provider calls; this slice preserves existing request mutation behavior and only moves pure policy decisions. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal architecture slice. PR-specific GHAS checks will be monitored after opening. |
||
|
|
b1e871d51c
|
refactor(proxy): isolate memory rank policy (#1960)
## Description Extracts the proxy memory ranking formulas into a pure `memory_rank_policy` module and keeps `MemoryCandidate` / `RecencyBoostRanker` as the public adapter-facing API. Also preserves backend memory IDs when ranked candidates are rebuilt, so downstream memory update/delete handles survive the ranking boundary. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.memory_rank_policy` for timestamp parsing, recency factor calculation, and score boosting. - Updated `RecencyBoostRanker` to delegate policy math while preserving the existing public API. - Preserved `MemoryCandidate.id` when rank output candidates are rebuilt. - Added focused policy tests plus an ID-preservation regression test. - Included the current LiteLLM callback signature compatibility shim required for repo-wide mypy on main-based slices. ## 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 python -m pytest tests/test_memory_rank_policy.py tests/test_memory_ranker.py tests/test_litellm_callback.py -q 32 passed in 6.22s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local worktree based on `headroomlabs/main`. - Exact command / steps: targeted pytest, ruff, ruff format check, repo-wide mypy, staged gitleaks scan. - Observed result: memory rank policy/ranker/callback tests pass; static checks pass; no staged secrets detected. - Not tested: full provider/API integration; this slice only changes pure policy delegation and candidate shape preservation. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal architecture slice. PR-specific GHAS checks will be monitored after opening. |
||
|
|
1c1e360112
|
refactor(proxy): isolate project attribution policy (#1957)
## Description Extracts pure project attribution policy from the runtime project context holder. Header classification, project path splitting, and project-prefixed base URL construction now live in a policy module while `project_context` keeps the ContextVar and ASGI scope adapter responsibilities. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.project_policy` for pure project attribution header/path/base-URL helpers. - Updated `headroom.proxy.project_context` to re-export the pure helpers and retain only request context binding and ASGI scope mutation. - Added direct tests for the extracted project attribution policy boundary. - Kept the LiteLLM callback compatibility shim required for repo-wide type checking on fresh branches. ## 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 python -m pytest tests/test_project_policy.py tests/test_proxy_project_savings.py tests/test_litellm_callback.py tests/test_compress_api.py::TestLiteLLMCallback -q 29 passed in 13.70s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local worktree based on `headroomlabs/main`. - Exact command / steps: ran focused project policy tests, project savings tests, LiteLLM callback tests, Ruff lint/format checks, mypy over `headroom`, and staged gitleaks scan. - Observed result: all local checks passed; staged secret scan found no leaks. - Not tested: full CI matrix and deployment flows; those are covered by GitHub Actions. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. GitHub reported existing Dependabot alerts on the default branch during push; this PR does not change dependencies, and the staged secret scan is clean. |
||
|
|
740fb9bc16
|
refactor(cache): isolate semantic key policy (#1953)
## Description Extracts proxy semantic-cache key normalization and hashing into a pure policy module while preserving `SemanticCache._compute_key` for existing callers and tests. This separates deterministic cache-key construction from the async cache adapter and LRU storage concerns. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.semantic_cache_key` for pure cache-control stripping and semantic cache key construction. - Updated `SemanticCache._compute_key` to delegate to the extracted policy while preserving the local `_strip_cache_control` compatibility alias. - Added direct tests for the extracted semantic-cache key policy. - Kept the LiteLLM callback compatibility shim required for repo-wide type checking on fresh branches. ## 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 python -m pytest tests/test_proxy_semantic_cache_key_policy.py tests/test_proxy_semantic_cache_key.py tests/test_proxy_semantic_cache_key_integration.py tests/test_proxy_openai_cache_key_integration.py tests/test_litellm_callback.py tests/test_compress_api.py::TestLiteLLMCallback -q 46 passed in 11.83s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local worktree based on `headroomlabs/main`. - Exact command / steps: ran focused semantic cache key tests, handler cache-key integration tests, LiteLLM callback tests, Ruff lint/format checks, mypy over `headroom`, and staged gitleaks scan. - Observed result: all local checks passed; staged secret scan found no leaks. - Not tested: full CI matrix and deployment flows; those are covered by GitHub Actions. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. GitHub reported existing Dependabot alerts on the default branch during push; this PR does not change dependencies, and the staged secret scan is clean. |
||
|
|
ea1951508b
|
refactor(proxy): isolate rate limit policy (#1954)
## Description Extracts token-bucket refill, consume, wait-time, and stale-bucket selection formulas into a pure rate-limit policy module while preserving the async `TokenBucketRateLimiter` adapter for locks and mutable bucket storage. 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 - [x] Code refactoring (no functional changes) ## Changes Made - Added `headroom.proxy.rate_limit_policy` for pure token-bucket calculations. - Updated `TokenBucketRateLimiter` to delegate refill, consume, and stale-key selection to the extracted policy. - Added direct tests for the rate-limit policy boundary. - Kept the LiteLLM callback compatibility shim required for repo-wide type checking on fresh branches. ## 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 python -m pytest tests/test_rate_limit_policy.py tests/test_proxy_healthchecks.py tests/test_litellm_callback.py tests/test_compress_api.py::TestLiteLLMCallback -q 27 passed in 17.82s python -m ruff check . All checks passed! python -m ruff format --check . 1095 files already formatted python -m mypy headroom --ignore-missing-imports Success: no issues found in 409 source files gitleaks protect --staged --no-banner --redact no leaks found ``` ## Real Behavior Proof - Environment: Windows, Python 3.13.13, local worktree based on `headroomlabs/main`. - Exact command / steps: ran focused rate-limit policy tests, proxy health checks, LiteLLM callback tests, Ruff lint/format checks, mypy over `headroom`, and staged gitleaks scan. - Observed result: all local checks passed; staged secret scan found no leaks. - Not tested: full CI matrix and deployment flows; those are covered by GitHub Actions. ## 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 - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) N/A ## Additional Notes Documentation and changelog updates are not applicable for this internal refactor. GitHub reported existing Dependabot alerts on the default branch during push; this PR does not change dependencies, and the staged secret scan is clean. |