Conversation
Admin-supplied `http_headers` in the managed coding-agent config were parsed and
publishable but never reached any agent. Wire them into Claude Code:
`managed_state_overrides` now emits `{tool}_http_headers`, and `render_overlay`
merges them into `ANTHROPIC_CUSTOM_HEADERS`.
Admin headers win on a case-insensitive name collision, replacing ucode's own
line in place and appending any new ones. The header names ucode writes are
tracked in state so a header an admin later removes is dropped rather than
lingering as an unrecognized user header, while hand-added user headers are
preserved.
Co-authored-by: Isaac <no-reply@databricks.com>
Reduce the inline commentary added with the managed http_headers wiring. Add a capture-server check to tests/test_e2e_user_agent.py that launches the real Claude binary and asserts an admin-supplied managed header arrives on the inference request, alongside the existing User-Agent assertion. Co-authored-by: Isaac <no-reply@databricks.com>
Extend tests/test_e2e_user_agent.py so the capture-server harness launches the real Claude binary and asserts an admin-supplied managed header arrives on the inference request, alongside the existing User-Agent assertion. Adds a shared case-insensitive header lookup helper. Co-authored-by: Isaac <no-reply@databricks.com>
The prior removal tracking stored the header names ucode wrote in ug state, but ug state is not reliably preserved across runs, so a removed managed header lingered while the new one was added (both appeared). Derive the ucode-owned header set from the files instead: ucode owns its own ucode-settings.json wholesale (every header there is ucode's, so a dropped managed header cannot linger), and for the OS-managed file only the names ucode last wrote (last_applied_by_ug) are treated as ucode's, leaving IT-authored headers untouched. Removes the ug-state tracking key entirely. Co-authored-by: Isaac <no-reply@databricks.com>
tt-le
added a commit
that referenced
this pull request
Sep 21, 2026
Wire the managed coding-agent config's `http_headers` into Codex, independent of
the Claude change: `managed_state_overrides` emits `{tool}_http_headers`, and
`_provider_block` merges them into the Databricks provider's `[http_headers]`
table (current and legacy layouts).
Admin headers win on a case-insensitive name collision, replacing ucode's own
header (including routing headers) rather than emitting two spellings of one HTTP
header. ug is the sole author of that table, so it is pruned wholesale before
each merge and rewritten from render_overlay — dropping routing headers that no
longer apply and any admin header removed from managed config, with no ug state.
Adds a wire-level check to tests/test_e2e_user_agent.py that launches the real
Codex binary and asserts an admin header arrives on the inference request.
Note: shares the small managed_resolve plumbing and the `_header` test helper
with the Claude PR (#772); whichever merges second
resolves a trivial overlap.
Co-authored-by: Isaac <no-reply@databricks.com>
Removes the duplicate `if tool in ("claude", "codex")` in managed_state_overrides
by grouping the http_headers override with static_models. No behavior change; the
override is still emitted only for the agents whose writers consume it.
Co-authored-by: Isaac <no-reply@databricks.com>
Emit `{tool}_http_headers` for any tool rather than only claude/codex. Only those
writers consume it today, so the override is inert for other agents, and dropping
the gate keeps the resolver simple.
Co-authored-by: Isaac <no-reply@databricks.com>
CLAUDE_MANAGED_CUSTOM_HEADER_NAMES is a frozenset, so the bare union (frozenset | set | set) evaluated to frozenset[str], violating the declared -> set[str] return type. Wrap the union in set(...). Co-authored-by: Isaac <no-reply@databricks.com>
tt-le
added a commit
that referenced
this pull request
Sep 21, 2026
## What Wire the managed coding-agent config's `http_headers` into **Codex**. Independent of the Claude PR (#772) — both target `main`. - `managed_resolve.managed_state_overrides` emits `{tool}_http_headers`. - `codex.write_tool_config` reads `codex_http_headers`; `_provider_block` merges the admin headers into the Databricks provider's `[http_headers]` table (current per-profile layout and legacy `< 0.134.0` layout). ## Behavior - **Admin wins on a case-insensitive name collision** — a managed header replaces ucode's own header (including routing headers), never emitting two spellings of one HTTP header. - **Clean removal, no state** — ug is the sole author of the provider `http_headers` table, so it's pruned wholesale before each merge and rewritten from `render_overlay`. This drops routing headers that no longer apply and any admin header removed from managed config; `deep_merge` alone cannot delete keys. ## Tests - `render_overlay`: admin header added; admin overrides ucode's routing header case-insensitively with no duplicate spelling. - `write_tool_config` end-to-end: admin header written to `config.toml`; dropped after removal while `User-Agent` is kept. - Wire-level (`tests/test_e2e_user_agent.py`): the capture-server harness launches the real Codex binary and asserts an admin header arrives on the inference request. ## Note on overlap with #772 This PR shares the small `managed_resolve` plumbing (the `{tool}_http_headers` emit) and the `_header` test helper with #772. They're identical in both; whichever merges second resolves a trivial overlap. Neither depends on the other. This pull request and its description were written by Isaac. --------- Co-authored-by: Isaac <no-reply@databricks.com>
…ership ug now writes the entire ANTHROPIC_CUSTOM_HEADERS value (its static headers plus the admin's managed http_headers, already merged by render_overlay) to both ucode-settings.json and the OS managed-settings.json, dropping the per-header ownership inference. The previous approach derived "names ug owns" from snapshots.last_applied_by_ug, which records the whole previously-written merged document. A foreign/admin header ug merely passed through on the first write was therefore adopted into the owned set and silently dropped on the next reconcile -- the opposite of the intended "leave an administrator's own headers untouched". Overwriting the whole value removes the need to infer ownership, so a header ug no longer emits is dropped and nothing stale or foreign lingers. This matches Codex's full-replace model; env.ANTHROPIC_CUSTOM_HEADERS is already a ug-owned managed key path, so the wholesale write is authorized. Intentional behavior change: a custom header must arrive through the managed config http_headers manifest to appear. A header hand-edited directly into the managed file's ANTHROPIC_CUSTOM_HEADERS value is no longer preserved. Removes CLAUDE_MANAGED_CUSTOM_HEADER_NAMES, _custom_header_names, _managed_header_names, and _merge_anthropic_custom_headers. Rewrites the managed-file header tests and adds a real three-generation reconcile round-trip. Co-authored-by: Isaac <no-reply@databricks.com>
write_tool_config now decides how to write the Claude custom-header value from whether an admin managed config is present this launch (refresh_managed_config(state).manifest is not None, reusing the launch's warm cache; a failed fetch degrades to unmanaged and never blocks the write): - managed config present: overwrite the value wholesale with ug's static headers plus the admin's managed http_headers, so a header ug no longer emits is dropped and nothing stale or foreign lingers. - no managed config: restore the pre-managed-config preserving merge -- keep the developer's own pre-existing headers, replacing only the header names ug manages (the static CLAUDE_MANAGED_CUSTOM_HEADER_NAMES set). This brings back the behavior from #442 for the unmanaged case without reintroducing the removed per-header ownership inference that dropped foreign headers on reconcile. existing_custom_headers is read before deep_merge_dict mutates base["env"] in place; reading it afterward would see the overlay's own value and collapse the preserve merge into an overwrite. Co-authored-by: Isaac <no-reply@databricks.com>
The static-name merge is covered by TestMergeAnthropicCustomHeaders unit tests, so the write_tool_config-level tests only need one gate test per branch plus the real-reconcile integration test. Remove three duplicates: - test_managed_file_overwrites_anthropic_custom_headers_wholesale (subsumed by test_managed_file_overwrites_dropping_foreign_and_removed_headers) - test_unmanaged_drops_stale_ucode_managed_header_no_longer_emitted (pure merge logic, covered by the unit test) - test_unmanaged_wholesale_preserve_survives_real_reconcile_round_trip (reconcile plumbing covered by the managed round-trip; unmanaged preserve by unit + gate) Co-authored-by: Isaac <no-reply@databricks.com>
Pure repositioning, no behavior change: the earlier wholesale->conditional detour deleted and re-added _merge_anthropic_custom_headers (and its TestMergeAnthropicCustomHeaders suite), landing them far from where main keeps them and inflating the review diff. Move the helper back below write_tool_config and restore the test class to its original spot (matching main verbatim), and drop two stray comments so unchanged code shows no diff. The real change is now just the managed-config gate in write_tool_config plus _apply_managed_header_lines. Co-authored-by: Isaac <no-reply@databricks.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Admin-supplied
http_headersin the managed coding-agent config were parsed and publishable but never reached any agent. Wire them into Claude Code. Independent of the Codex PR (#773) — both targetmain.managed_resolve.managed_state_overridesemits{tool}_http_headers.claude.render_overlaymerges the admin headers intoANTHROPIC_CUSTOM_HEADERS.Behavior
ucode-settings.jsonwholesale (every header there is ucode's, so a dropped managed header can't linger), and for the OS-managed file only the names ucode last wrote (last_applied_by_ug) are treated as ucode's, leaving IT-authored headers untouched. (An earlier revision tracked names in ug state; that isn't preserved across runs, so a removed header lingered — fixed here.)Tests
render_overlay/_merge_anthropic_custom_headers: add, in-place override, removal, IT-header preservation.write_tool_config: admin header written; dropped after removal on the ucode file; IT header preserved on the managed file vialast_applied_by_ug.managed_resolve:http_headers→{tool}_http_headersoverride.tests/test_e2e_user_agent.py): the capture-server harness launches the real Claude binary and asserts an admin header arrives on the inference request.Note on overlap with #773
Shares the small
managed_resolveplumbing and the_headertest helper with #773; identical in both, trivial overlap on whichever merges second. Neither depends on the other.This pull request and its description were written by Isaac.