Opus 4.7 commonly narrates before calling a tool ("Let me check the time for you..." → tool_use(get_time)). The orchestrator previously appended only the toolUse block to the messages array. On the next round-trip the model had no record of its narration — user follow-ups like "what did you say before checking the time?" got a denial.
Fix: introduce assistantTextSinceLastFlush parallel to assistantTextSoFar (cumulative for D-02 vision routing). On every toolUseRequested:
Flush the per-flush buffer into a .text(...) block
Bundle it with the .toolUse(...) block in the SAME assistant message
Trim whitespace-only text to avoid Anthropic 400s on empty text blocks
Reset the flush buffer so subsequent tool_uses in the same round get only the text emitted between them
The MIN_CHARS_FOR_CACHE_BREAKPOINT = 4096 threshold is structurally false on healthy boot (preamble ~700 chars, empty initial context), so eligibleForSystemPrompt returns nil on every turn and the prompt cache is dormant. Not a fix request per the audit — added a paragraph in CacheHints.swift explicitly documenting the dormancy, the intentional cost-acceptance, and the natural recovery once M-4 lands searchFacts-driven hydration.
Test plan
3 new tests in AssistantTextWithToolUseTests:
ATU-1 text+toolUse bundled into same assistant message
ATU-2 toolUse-alone produces no spurious empty text block
ATU-3 whitespace-only text trimmed (no Anthropic 400)
Full AgentCore suite: 248 tests, 0 failures, 1 skipped locally
CI green on the SPM AgentCore + Harness + Replay jobs (depends on Grogu queue)
## Summary
Two related AgentCore tactical fixes from the 2026-05-12 audit:
### #24 — Assistant text dropped alongside tool_use
Opus 4.7 commonly narrates before calling a tool ("Let me check the time for you..." → `tool_use(get_time)`). The orchestrator previously appended **only the toolUse block** to the messages array. On the next round-trip the model had no record of its narration — user follow-ups like "what did you say before checking the time?" got a denial.
**Fix:** introduce `assistantTextSinceLastFlush` parallel to `assistantTextSoFar` (cumulative for D-02 vision routing). On every `toolUseRequested`:
- Flush the per-flush buffer into a `.text(...)` block
- Bundle it with the `.toolUse(...)` block in the SAME assistant message
- Trim whitespace-only text to avoid Anthropic 400s on empty text blocks
- Reset the flush buffer so subsequent tool_uses in the same round get only the text emitted between them
- D-02 cumulative `assistantTextSoFar` untouched
### #23 — Cache hints dormancy documentation
The `MIN_CHARS_FOR_CACHE_BREAKPOINT = 4096` threshold is structurally false on healthy boot (preamble ~700 chars, empty initial context), so `eligibleForSystemPrompt` returns `nil` on every turn and the prompt cache is dormant. **Not a fix request per the audit** — added a paragraph in `CacheHints.swift` explicitly documenting the dormancy, the intentional cost-acceptance, and the natural recovery once M-4 lands `searchFacts`-driven hydration.
## Test plan
- [x] 3 new tests in `AssistantTextWithToolUseTests`:
- `ATU-1` text+toolUse bundled into same assistant message
- `ATU-2` toolUse-alone produces no spurious empty text block
- `ATU-3` whitespace-only text trimmed (no Anthropic 400)
- [x] Full AgentCore suite: **248 tests, 0 failures, 1 skipped** locally
- [ ] CI green on the SPM AgentCore + Harness + Replay jobs (depends on Grogu queue)
Closes #23, #24
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Two related AgentCore tactical fixes from the 2026-05-12 audit:
#24 — Assistant text dropped alongside tool_use
Opus 4.7 commonly narrates before calling a tool ("Let me check the time for you..." →
tool_use(get_time)). The orchestrator previously appended only the toolUse block to the messages array. On the next round-trip the model had no record of its narration — user follow-ups like "what did you say before checking the time?" got a denial.Fix: introduce
assistantTextSinceLastFlushparallel toassistantTextSoFar(cumulative for D-02 vision routing). On everytoolUseRequested:.text(...)block.toolUse(...)block in the SAME assistant messageassistantTextSoFaruntouched#23 — Cache hints dormancy documentation
The
MIN_CHARS_FOR_CACHE_BREAKPOINT = 4096threshold is structurally false on healthy boot (preamble ~700 chars, empty initial context), soeligibleForSystemPromptreturnsnilon every turn and the prompt cache is dormant. Not a fix request per the audit — added a paragraph inCacheHints.swiftexplicitly documenting the dormancy, the intentional cost-acceptance, and the natural recovery once M-4 landssearchFacts-driven hydration.Test plan
AssistantTextWithToolUseTests:ATU-1text+toolUse bundled into same assistant messageATU-2toolUse-alone produces no spurious empty text blockATU-3whitespace-only text trimmed (no Anthropic 400)Closes #23, #24
🤖 Generated with Claude Code