Skip to content

capture(claudecode): tool-result pairing is safe by accident, not by construction #36

Description

@JumpTechCode

parse registers &last.Tools[i] in a pending map so a result arriving on a
later record can be attached to the call it answers.

This is correct today, and was checked rather than assumed: append reallocates
the []Turn backing array, but Turn.Tools is its own slice and the copy
duplicates the header, not the array, so the registered pointer stays live. It
survives 200 forced reallocations between a call and its result.

The safety rests on an invariant nothing states: no code may append to a
Turn.Tools after that turn is stored. recordToTurn happens to build Tools
completely before the turn is appended. Any later change that adds a call to an
existing turn — merging streamed assistant messages would do exactly that —
reallocates Tools and orphans every registered pointer silently. Results would
attach to a stale array with no error anywhere.

Fix

Key by position and resolve at the end, which also removes the one-field
pendingCall wrapper:

pending := map[string][2]int{} // tool_use id -> {turn index, tool index}
...
pending[ids[i]] = [2]int{len(s.Turns) - 1, i}
...
s.Turns[p[0]].Tools[p[1]].Result = res

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    capture-adapterThe Claude Code capture adapterenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions