Keep deferred tool entries in place when a `defer_loading=True` capability is loaded on Anthropic
- Closes #11087
On Anthropic without a tool-search surface, resolve_request_tools declares a defer_loading=True capability's tools as defer_loading entries from the first request. When load_capability revealed them, _revealed_deferred_tool_order moved them to the tail of tools in reveal order (and load_capability reveals in name order), so the deferred entries earlier requests sent came back reordered. Anthropic leaves deferred entries out of its cache key, but the wire contract is append-only (anthropic_deferred_tool_blocks).
Now only a tool the reveal first declares moves to the tail: a mid-run newcomer, or a deferred tool that was withheld because a tool-search surface is on the wire. A tool already declared deferred stays where earlier requests sent it.
Reproduction (before this PR)
Two deferred capabilities, the first loaded, with tools declared out of name order (write_note, read_note, delete_note, then look_up_archive):
| Request | Deferred entries |
|---|---|
| 1 (before load) | write_note, read_note, delete_note, look_up_archive |
| 2 (after load), before | look_up_archive, delete_note, read_note, write_note |
| 2 (after load), this PR | write_note, read_note, delete_note, look_up_archive |
Fixing only the name-order sort in load_capability isn't enough: the tail move still reorders whenever the loaded capability isn't the last deferred one declared.
Tests
tests/test_history_append_only.pygets aVARIANTStable with aCapability(defer_loading=True)case: the two-capability scenario above, loaded in the first run and called in the second. Anthropic and OpenAI Responses pass.
OpenAI Chat
Chat Completions has no deferred-tool channel, so a load adds the capability's tools to tools, ahead of the messages already sent. That's the same one-time move #9405 makes for mid-run newcomers on Chat. The move is now an ALLOWED entry in the append-only matrix, limited to one move per load and only on openai-chat. The cache table in docs/capabilities/on-demand.md now says such a load breaks the prefix once, instead of "may break between turns".
Checklist
- No breaking changes in accordance with the version policy.
- Any permitted compatibility impact has a label, warning, migration, release note, and exact API-check waiver.
- PR title is fit for the release changelog.
š¤ Generated with Claude Code