fix(agent): don't let a materialized default budget defeat context-window scaling#4122
Conversation
…aling odysseus-dev#1230 scales agent_input_token_budget to the model's context window unless the user explicitly set a budget, detected via is_setting_overridden(). But the settings-save path materializes every DEFAULT_SETTINGS key into settings.json (load_settings merges defaults; handlers persist the merged dict), so the persisted default 6000 reads as "overridden" and the budget code takes the min(6000, ctx) branch — silently re-capping long-context models at 6000 for anyone who has ever saved a setting. This reintroduces the exact regression odysseus-dev#1170/odysseus-dev#1230 set out to fix. Add is_setting_customized() (saved value != default) and gate the scaling on it instead of mere presence. A persisted default is not a user choice. is_setting_overridden has exactly one consumer (this budget path), so the change is contained. Tests cover the materialized-default regression, a deliberately-chosen budget still being honoured, and the absent-key case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I think this is fixing a real regression, but there are two places where the new routing still needs tighter semantics before this is safe to rely on broadly. Findings
Open Questions
ValidationRan:
Not run:
|
Address RaresKeY's review: P2 (explicitness): is_setting_customized treated a saved value equal to the default as "not explicit", which ALSO blocked a user from deliberately pinning the default budget. Reframe the default value itself as the AUTO sentinel — agent_input_token_budget == DEFAULT_BUDGET means "scale to the model's context window", any other value is an explicit cap. A materialized default still reads as auto (fixing the original regression), and any non-default value the user chooses is now honoured. Drop the now-unused is_setting_customized helper. P2 (fallback context): auto-scaling trusted get_context_length() even when it returned only the bare DEFAULT_CONTEXT fallback (no endpoint-reported / known window), over-allocating on self-hosted/proxy setups. Add get_context_length_known() (also returns whether the window was actually discovered); the budget block passes 0 when unknown so auto-scaling stays conservative instead of inflating to an unproven window. hard_max stays auto-only — a deliberate explicit budget wins (odysseus-dev#1190); kept that contract and answered the reviewer's question rather than silently reversing it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @RaresKeY — reworked in 3c6b3f0 (supersedes the original P2 (explicitness). You're right that The one thing this gives up is pinning exactly the default — to cap near 6000 you'd set e.g. 5999. I did weigh the fuller "explicitness regardless of value" route (your option 1): it needs either a one-time migration to relabel already-materialized P2 (fallback context). Good catch — auto-scaling was trusting This is intentionally the minimal trusted-vs-fallback guard — which is also my answer to your scope question: the authoritative "real served context / auto input budget / max tokens" belongs in the #2739 capability metadata, so I'd defer the richer provider-capability version to that bucket rather than grow it here. On |
|
I found one current-head correctness issue in the new fallback-context guard. P2 issue: The known-context guard can still budget from the stale fallback value
_, _ctx_known = get_context_length_known(endpoint_url, model)
ctx_for_budget = context_length if _ctx_known else 0That splits the provenance bit from the value it proves. The earlier The same wiring also leaves direct/background Please bind the known flag to the same context value used for budget computation, either by using the returned value from Small cleanup: comments/docs still need to match the new contract. |
odysseus-dev#4121) Per WGlynn's review on the issue: add an end-to-end regression that saves an UNRELATED setting (which makes the settings-save path materialize the budget default into settings.json) and asserts the budget still auto-scales rather than re-reading as an explicit 6000 cap — locking the exact reopening shut. To make the test bite the production decision (not just re-derive it), extract `budget_is_explicit()` into src/context_budget.py and use it from the agent loop. It keys off value-vs-default (the default is the auto sentinel), NOT settings presence — which is the whole point, since the save path materializes defaults. Note: after this PR's rework, is_setting_overridden has ZERO production callers, so the merged-dict materialization smell can't reach any setting through a presence check today (WGlynn's durability concern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…iew odysseus-dev#4122) RaresKeY caught a correctness bug in the fallback-context guard: stream_agent_loop kept only the `known` flag from get_context_length_known() and budgeted off the passed-in `context_length`, which can come from a *different* lookup. Two failures: - local endpoints are re-queried, so the passed value can be a stale DEFAULT_CONTEXT fallback while the fresh probe proves the real (smaller) served context — we'd scale off the stale value; - callers that don't pass context_length (scheduled tasks, teacher escalation, skill test runs, bg_monitor) were capped at 6000 even when a long window is discoverable. Extract budget_context_for_model() which returns the freshly-probed window when known else 0, binding the flag to the value it proves; the agent loop uses it. Regression tests cover the stale-fallback, no-arg-caller, and probe-error paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Good catch, thanks — fixed in 12cd741. You're right that the guard split the provenance bit from the value it proves. I extracted
Regression tests in Also did the cleanup: refreshed the budget-block comments to the new contract (default value = auto sentinel, non-default = explicit cap, |
|
Runtime fix looks good on the paths I checked. One P3 cleanup: Nit while touching those comments: the new budget comments are pretty verbose. Could you tighten them to the contract only: default |
…iew odysseus-dev#4122) - settings.py: an explicit budget is clamped to the window only — hard_max is auto-only (odysseus-dev#1190); drop the incorrect "and to hard_max". - is_setting_overridden docstring: drop the stale "adaptive budgets" example; point value-sensitive callers at context_budget.budget_is_explicit. - Tighten the budget-block comments to the contract (default = auto sentinel, non-default = explicit cap, hard_max = auto-only ceiling). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
odysseus-dev#1230/odysseus-dev#1273) The context-budget contract (auto-sentinel, explicit budgets honoured, hard_max auto-only) merged via odysseus-dev#1230 — odysseus-dev#1190 was the earlier, closed, superseded PR. Re-point the contract comments at odysseus-dev#1230 (the live source, already cited for the auto-sentinel two lines up in settings.py). The configurable hard_max setting (`agent_input_token_hard_max`) was a reviewer requirement first raised on odysseus-dev#1190, omitted from the merged odysseus-dev#1230, and actually added in odysseus-dev#1273 — credit odysseus-dev#1273 for it and correct the test comment's history (it previously implied this PR completed the requirement). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks — all three addressed (comment-only), plus one I caught while in there.
While tightening I noticed the comments were citing Will keep comments tight to the contract and the issue refs pointing at the merged source going forward — appreciate the nudges. |
RaresKeY
left a comment
There was a problem hiding this comment.
LGTM.
This is in my local merge queue. There’s an unrelated merge blocker right now, so I’ll merge it later once that clears.
pewdiepie-archdaemon
left a comment
There was a problem hiding this comment.
Looks good. I rechecked the final version after the review fixes: the default budget is now treated as the auto sentinel, fallback context no longer inflates the auto budget, and the known-context helper binds provenance to the value used for budgeting. Focused local suite passed: tests/test_budget_auto_sentinel.py tests/test_context_budget.py tests/test_model_context.py tests/test_context_cache_per_endpoint.py tests/test_llama_server_models_url.py (55 passed).
…ndow scaling (odysseus-dev#4122) * fix(agent): don't let a materialized default budget defeat context scaling odysseus-dev#1230 scales agent_input_token_budget to the model's context window unless the user explicitly set a budget, detected via is_setting_overridden(). But the settings-save path materializes every DEFAULT_SETTINGS key into settings.json (load_settings merges defaults; handlers persist the merged dict), so the persisted default 6000 reads as "overridden" and the budget code takes the min(6000, ctx) branch — silently re-capping long-context models at 6000 for anyone who has ever saved a setting. This reintroduces the exact regression odysseus-dev#1170/odysseus-dev#1230 set out to fix. Add is_setting_customized() (saved value != default) and gate the scaling on it instead of mere presence. A persisted default is not a user choice. is_setting_overridden has exactly one consumer (this budget path), so the change is contained. Tests cover the materialized-default regression, a deliberately-chosen budget still being honoured, and the absent-key case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): rework context-budget fix per review (odysseus-dev#4122) Address RaresKeY's review: P2 (explicitness): is_setting_customized treated a saved value equal to the default as "not explicit", which ALSO blocked a user from deliberately pinning the default budget. Reframe the default value itself as the AUTO sentinel — agent_input_token_budget == DEFAULT_BUDGET means "scale to the model's context window", any other value is an explicit cap. A materialized default still reads as auto (fixing the original regression), and any non-default value the user chooses is now honoured. Drop the now-unused is_setting_customized helper. P2 (fallback context): auto-scaling trusted get_context_length() even when it returned only the bare DEFAULT_CONTEXT fallback (no endpoint-reported / known window), over-allocating on self-hosted/proxy setups. Add get_context_length_known() (also returns whether the window was actually discovered); the budget block passes 0 when unknown so auto-scaling stays conservative instead of inflating to an unproven window. hard_max stays auto-only — a deliberate explicit budget wins (odysseus-dev#1190); kept that contract and answered the reviewer's question rather than silently reversing it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(agent): lock the materialized-default budget regression (review on odysseus-dev#4121) Per WGlynn's review on the issue: add an end-to-end regression that saves an UNRELATED setting (which makes the settings-save path materialize the budget default into settings.json) and asserts the budget still auto-scales rather than re-reading as an explicit 6000 cap — locking the exact reopening shut. To make the test bite the production decision (not just re-derive it), extract `budget_is_explicit()` into src/context_budget.py and use it from the agent loop. It keys off value-vs-default (the default is the auto sentinel), NOT settings presence — which is the whole point, since the save path materializes defaults. Note: after this PR's rework, is_setting_overridden has ZERO production callers, so the merged-dict materialization smell can't reach any setting through a presence check today (WGlynn's durability concern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): bind the budget context window to its own provenance (review odysseus-dev#4122) RaresKeY caught a correctness bug in the fallback-context guard: stream_agent_loop kept only the `known` flag from get_context_length_known() and budgeted off the passed-in `context_length`, which can come from a *different* lookup. Two failures: - local endpoints are re-queried, so the passed value can be a stale DEFAULT_CONTEXT fallback while the fresh probe proves the real (smaller) served context — we'd scale off the stale value; - callers that don't pass context_length (scheduled tasks, teacher escalation, skill test runs, bg_monitor) were capped at 6000 even when a long window is discoverable. Extract budget_context_for_model() which returns the freshly-probed window when known else 0, binding the flag to the value it proves; the agent loop uses it. Regression tests cover the stale-fallback, no-arg-caller, and probe-error paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agent): fix stale budget comments + tighten to the contract (review odysseus-dev#4122) - settings.py: an explicit budget is clamped to the window only — hard_max is auto-only (odysseus-dev#1190); drop the incorrect "and to hard_max". - is_setting_overridden docstring: drop the stale "adaptive budgets" example; point value-sensitive callers at context_budget.budget_is_explicit. - Tighten the budget-block comments to the contract (default = auto sentinel, non-default = explicit cap, hard_max = auto-only ceiling). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agent): correct budget issue citations (odysseus-dev#1190 → merged odysseus-dev#1230/odysseus-dev#1273) The context-budget contract (auto-sentinel, explicit budgets honoured, hard_max auto-only) merged via odysseus-dev#1230 — odysseus-dev#1190 was the earlier, closed, superseded PR. Re-point the contract comments at odysseus-dev#1230 (the live source, already cited for the auto-sentinel two lines up in settings.py). The configurable hard_max setting (`agent_input_token_hard_max`) was a reviewer requirement first raised on odysseus-dev#1190, omitted from the merged odysseus-dev#1230, and actually added in odysseus-dev#1273 — credit odysseus-dev#1273 for it and correct the test comment's history (it previously implied this PR completed the requirement). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndow scaling (odysseus-dev#4122) * fix(agent): don't let a materialized default budget defeat context scaling odysseus-dev#1230 scales agent_input_token_budget to the model's context window unless the user explicitly set a budget, detected via is_setting_overridden(). But the settings-save path materializes every DEFAULT_SETTINGS key into settings.json (load_settings merges defaults; handlers persist the merged dict), so the persisted default 6000 reads as "overridden" and the budget code takes the min(6000, ctx) branch — silently re-capping long-context models at 6000 for anyone who has ever saved a setting. This reintroduces the exact regression odysseus-dev#1170/odysseus-dev#1230 set out to fix. Add is_setting_customized() (saved value != default) and gate the scaling on it instead of mere presence. A persisted default is not a user choice. is_setting_overridden has exactly one consumer (this budget path), so the change is contained. Tests cover the materialized-default regression, a deliberately-chosen budget still being honoured, and the absent-key case. * fix(agent): rework context-budget fix per review (odysseus-dev#4122) Address RaresKeY's review: P2 (explicitness): is_setting_customized treated a saved value equal to the default as "not explicit", which ALSO blocked a user from deliberately pinning the default budget. Reframe the default value itself as the AUTO sentinel — agent_input_token_budget == DEFAULT_BUDGET means "scale to the model's context window", any other value is an explicit cap. A materialized default still reads as auto (fixing the original regression), and any non-default value the user chooses is now honoured. Drop the now-unused is_setting_customized helper. P2 (fallback context): auto-scaling trusted get_context_length() even when it returned only the bare DEFAULT_CONTEXT fallback (no endpoint-reported / known window), over-allocating on self-hosted/proxy setups. Add get_context_length_known() (also returns whether the window was actually discovered); the budget block passes 0 when unknown so auto-scaling stays conservative instead of inflating to an unproven window. hard_max stays auto-only — a deliberate explicit budget wins (odysseus-dev#1190); kept that contract and answered the reviewer's question rather than silently reversing it. * test(agent): lock the materialized-default budget regression (review on odysseus-dev#4121) Per WGlynn's review on the issue: add an end-to-end regression that saves an UNRELATED setting (which makes the settings-save path materialize the budget default into settings.json) and asserts the budget still auto-scales rather than re-reading as an explicit 6000 cap — locking the exact reopening shut. To make the test bite the production decision (not just re-derive it), extract `budget_is_explicit()` into src/context_budget.py and use it from the agent loop. It keys off value-vs-default (the default is the auto sentinel), NOT settings presence — which is the whole point, since the save path materializes defaults. Note: after this PR's rework, is_setting_overridden has ZERO production callers, so the merged-dict materialization smell can't reach any setting through a presence check today (WGlynn's durability concern). * fix(agent): bind the budget context window to its own provenance (review odysseus-dev#4122) RaresKeY caught a correctness bug in the fallback-context guard: stream_agent_loop kept only the `known` flag from get_context_length_known() and budgeted off the passed-in `context_length`, which can come from a *different* lookup. Two failures: - local endpoints are re-queried, so the passed value can be a stale DEFAULT_CONTEXT fallback while the fresh probe proves the real (smaller) served context — we'd scale off the stale value; - callers that don't pass context_length (scheduled tasks, teacher escalation, skill test runs, bg_monitor) were capped at 6000 even when a long window is discoverable. Extract budget_context_for_model() which returns the freshly-probed window when known else 0, binding the flag to the value it proves; the agent loop uses it. Regression tests cover the stale-fallback, no-arg-caller, and probe-error paths. * docs(agent): fix stale budget comments + tighten to the contract (review odysseus-dev#4122) - settings.py: an explicit budget is clamped to the window only — hard_max is auto-only (odysseus-dev#1190); drop the incorrect "and to hard_max". - is_setting_overridden docstring: drop the stale "adaptive budgets" example; point value-sensitive callers at context_budget.budget_is_explicit. - Tighten the budget-block comments to the contract (default = auto sentinel, non-default = explicit cap, hard_max = auto-only ceiling). Comment/docstring-only; no behaviour change. * docs(agent): correct budget issue citations (odysseus-dev#1190 → merged odysseus-dev#1230/odysseus-dev#1273) The context-budget contract (auto-sentinel, explicit budgets honoured, hard_max auto-only) merged via odysseus-dev#1230 — odysseus-dev#1190 was the earlier, closed, superseded PR. Re-point the contract comments at odysseus-dev#1230 (the live source, already cited for the auto-sentinel two lines up in settings.py). The configurable hard_max setting (`agent_input_token_hard_max`) was a reviewer requirement first raised on odysseus-dev#1190, omitted from the merged odysseus-dev#1230, and actually added in odysseus-dev#1273 — credit odysseus-dev#1273 for it and correct the test comment's history (it previously implied this PR completed the requirement). Comment/docstring-only; no behaviour change. ---------
…ndow scaling (odysseus-dev#4122) * fix(agent): don't let a materialized default budget defeat context scaling odysseus-dev#1230 scales agent_input_token_budget to the model's context window unless the user explicitly set a budget, detected via is_setting_overridden(). But the settings-save path materializes every DEFAULT_SETTINGS key into settings.json (load_settings merges defaults; handlers persist the merged dict), so the persisted default 6000 reads as "overridden" and the budget code takes the min(6000, ctx) branch — silently re-capping long-context models at 6000 for anyone who has ever saved a setting. This reintroduces the exact regression odysseus-dev#1170/odysseus-dev#1230 set out to fix. Add is_setting_customized() (saved value != default) and gate the scaling on it instead of mere presence. A persisted default is not a user choice. is_setting_overridden has exactly one consumer (this budget path), so the change is contained. Tests cover the materialized-default regression, a deliberately-chosen budget still being honoured, and the absent-key case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): rework context-budget fix per review (odysseus-dev#4122) Address RaresKeY's review: P2 (explicitness): is_setting_customized treated a saved value equal to the default as "not explicit", which ALSO blocked a user from deliberately pinning the default budget. Reframe the default value itself as the AUTO sentinel — agent_input_token_budget == DEFAULT_BUDGET means "scale to the model's context window", any other value is an explicit cap. A materialized default still reads as auto (fixing the original regression), and any non-default value the user chooses is now honoured. Drop the now-unused is_setting_customized helper. P2 (fallback context): auto-scaling trusted get_context_length() even when it returned only the bare DEFAULT_CONTEXT fallback (no endpoint-reported / known window), over-allocating on self-hosted/proxy setups. Add get_context_length_known() (also returns whether the window was actually discovered); the budget block passes 0 when unknown so auto-scaling stays conservative instead of inflating to an unproven window. hard_max stays auto-only — a deliberate explicit budget wins (odysseus-dev#1190); kept that contract and answered the reviewer's question rather than silently reversing it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(agent): lock the materialized-default budget regression (review on odysseus-dev#4121) Per WGlynn's review on the issue: add an end-to-end regression that saves an UNRELATED setting (which makes the settings-save path materialize the budget default into settings.json) and asserts the budget still auto-scales rather than re-reading as an explicit 6000 cap — locking the exact reopening shut. To make the test bite the production decision (not just re-derive it), extract `budget_is_explicit()` into src/context_budget.py and use it from the agent loop. It keys off value-vs-default (the default is the auto sentinel), NOT settings presence — which is the whole point, since the save path materializes defaults. Note: after this PR's rework, is_setting_overridden has ZERO production callers, so the merged-dict materialization smell can't reach any setting through a presence check today (WGlynn's durability concern). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(agent): bind the budget context window to its own provenance (review odysseus-dev#4122) RaresKeY caught a correctness bug in the fallback-context guard: stream_agent_loop kept only the `known` flag from get_context_length_known() and budgeted off the passed-in `context_length`, which can come from a *different* lookup. Two failures: - local endpoints are re-queried, so the passed value can be a stale DEFAULT_CONTEXT fallback while the fresh probe proves the real (smaller) served context — we'd scale off the stale value; - callers that don't pass context_length (scheduled tasks, teacher escalation, skill test runs, bg_monitor) were capped at 6000 even when a long window is discoverable. Extract budget_context_for_model() which returns the freshly-probed window when known else 0, binding the flag to the value it proves; the agent loop uses it. Regression tests cover the stale-fallback, no-arg-caller, and probe-error paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agent): fix stale budget comments + tighten to the contract (review odysseus-dev#4122) - settings.py: an explicit budget is clamped to the window only — hard_max is auto-only (odysseus-dev#1190); drop the incorrect "and to hard_max". - is_setting_overridden docstring: drop the stale "adaptive budgets" example; point value-sensitive callers at context_budget.budget_is_explicit. - Tighten the budget-block comments to the contract (default = auto sentinel, non-default = explicit cap, hard_max = auto-only ceiling). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(agent): correct budget issue citations (odysseus-dev#1190 → merged odysseus-dev#1230/odysseus-dev#1273) The context-budget contract (auto-sentinel, explicit budgets honoured, hard_max auto-only) merged via odysseus-dev#1230 — odysseus-dev#1190 was the earlier, closed, superseded PR. Re-point the contract comments at odysseus-dev#1230 (the live source, already cited for the auto-sentinel two lines up in settings.py). The configurable hard_max setting (`agent_input_token_hard_max`) was a reviewer requirement first raised on odysseus-dev#1190, omitted from the merged odysseus-dev#1230, and actually added in odysseus-dev#1273 — credit odysseus-dev#1273 for it and correct the test comment's history (it previously implied this PR completed the requirement). Comment/docstring-only; no behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
#1230madeagent_input_token_budgetadapt to the model's context window unless the user set an explicit budget. But the settings-save path materializes everyDEFAULT_SETTINGSkey intodata/settings.json(load_settings()returns{**DEFAULT_SETTINGS, **saved}and the save handlers persist that merged dict), so once a user changes any setting the default budget (6000) is written to disk. Detecting "explicit" viais_setting_overridden(presence) then reads that materialized default as a deliberate choice →min(6000, context_length) = 6000→ long-context models are silently re-capped at 6000 input tokens (the exact regression#1170/#1230set out to fix; symptom: the agent loses earlier conversation past ~6000 tokens).Fix — the default value is the "auto" sentinel.
agent_input_token_budget == DEFAULT_BUDGET(6000) means "scale to the model's window"; any other value is an explicit cap (clamped to the window). A materialized default still reads as auto, and a non-default value the user chooses is honoured. (A deliberate 6000 can't be told from a materialized one by value, so the default reads as auto — pin a cap with a nearby value, e.g. 5999.) The decision is centralised inbudget_is_explicit()(src/context_budget.py);is_setting_overriddenno longer has any production caller.Two related correctness guards from review:
get_context_length_known()reports whether the window was actually discovered (endpoint-reported / known table) vs a bareDEFAULT_CONTEXTfallback;budget_context_for_model()binds that flag to the value it proves, so a fallback 128K (or a caller that doesn't pass a context length) doesn't inflate the budget.agent_input_token_hard_maxstays auto-only — it caps the auto-derived budget; a deliberate explicit budget is honoured (#1230).Target branch
dev, notmain.Linked Issue
Fixes #4121.
Type of Change
Checklist
dev.How to Test
dev, with a long-context model: change one setting in the Settings panel (materializesagent_input_token_budget: 6000intodata/settings.json). Before this PR a conversation past ~6000 input tokens logs[agent] soft-trimmed context: N -> ~878 tokens(capped); with this PR it logs the scaled budget (~111k for a 131072 window) and history is retained.agent_input_token_budgetto a non-default value (e.g. 20000) — it's honoured as an explicit cap (clamped to the window).python -m pytest tests/test_budget_auto_sentinel.py tests/test_context_budget.py tests/test_model_context.py -q— covers the auto sentinel, the materialized-default-after-saving-an-unrelated-setting regression, the fallback-context guard, and the provenance binding (no-arg caller / stale local re-query).Visual / UI changes
None — backend only (
src/agent_loop.py,src/context_budget.py,src/model_context.py,src/settings.py, tests).