Conversation
63ebda1 to
0383602
Compare
…salvage #92693, part 2) A check_fn verdict flip (credential lands, Docker daemon starts, OAuth login completes) never invalidated the get_tool_definitions memo or the executor's bridge-scope cache — the registry generation only moves on registry MUTATIONS, so the stale tool list survived for the process lifetime. Both cache sites now key on an aggregate TTL-cached snapshot of every probe's verdict (memoized in the registry; hot-path hit is one dict lookup). A flip propagates within probe TTL + snapshot TTL (~35s worst case) or immediately on invalidate_check_fn_cache(). Both sites re-check the snapshot after compute and skip the store on mismatch, so a verdict flipping mid-compute can't park a fresh result under a stale key. Note: when a tool's availability genuinely changes mid-conversation, the tool list changes with it — a one-time prompt-prefix bust for that conversation. That is the intended behavior of availability-gated tools (the alternative is the tool never appearing until restart); stable environments see zero change. Salvaged from #92693 by @alt-glitch with authorship preserved.
0383602 to
96ed052
Compare
૮ >ﻌ< ა ci reviewran on 96ed052 — fix(tools): stale tool list no longer survives an availabili
|
|
Closing on a policy decision (Teknium, Sep 2 2026): availability-gated tools are FROZEN for the life of a session; the tool list only changes on user-consented refresh ( Why not this direction: the change removes the memo's shielding rather than adding damping — every Plan: reopen #100638 (@JoaoMarcos44, |
Summary
Availability-gated tools (Docker-gated, credential-gated, OAuth-gated) now appear/disappear when their
check_fnverdict actually flips, instead of the tool list staying stale for the process lifetime. Split out of #92693 as its own PR because it is the one fix in that set with a caching consequence worth reviewing separately: when the environment genuinely changes mid-conversation, the tool list changes with it — a one-time prompt-prefix bust for that conversation. Stable environments see zero change; the memo keeps hitting and the tool list stays byte-identical.Root cause: both memo sites keyed on the registry
_generationcounter, which only moves on registry mutations — acheck_fnverdict can flip (credential lands, daemon starts) without any mutation, so the memo kept serving the stale list forever.Salvaged from #92693 by @alt-glitch, authorship preserved.
Changes
tools/registry.py:check_fn_verdict_snapshot()— aggregate snapshot of every cached probe verdict, with its own short-TTL memo (keyed on registry scope, probe scope, generation; cleared byinvalidate_check_fn_cache()). Hot-path hit is one dict lookup; a flaky probe coalesces to at most one live re-probe per window.model_tools.py:get_tool_definitionsmemo key includes the verdict snapshot; TOCTOU guard re-checks the snapshot after compute and skips the store on mismatch (a flip mid-compute can't park a fresh result under a stale key); generation re-take handles probes that lazily register tools.agent/tool_executor.py: the executor's bridge-scope cache (which tools a session may reach throughtool_call) gains the same snapshot + config-fingerprint key members and the same TOCTOU guard — the second site of the identical staleness class.tests/tools/test_availability_cache_staleness.py: 5 behavior-level tests (verdict flip changes tool definitions, scope cache observes flips and config changes, snapshot coalesces probes, memo still hits within TTL), red-verified in the original PR.Validation
test_availability_cache_staleness.py,test_get_tool_definitions_cache_isolation.py,test_terminal_tool_requirements.py,test_mcp_reload_refreshes_cached_agents.py,test_tool_search.py)Infographic