Skip to content

Resume repair collapses tool-call IDs reused across generations #383

Description

@Calmingstorm

Confirmed transcript-repair inconsistency

Reviewed master at 886c36d8ebe861aa987059a1744d45b78797baae (v4.7.0). Suggested priority: P2; narrow restart/resume trigger.

Chat accepts provider tool-call IDs reused in separate generations, and the ledger distinguishes operations by generation sequence plus call ID. Resume repair instead treats the ID alone as globally unique. A completed earlier result can cause an unmatched later call with the same ID to be skipped during repair.

Source: chat acceptance, generation-scoped ledger, ID-only resume maps.

Reproductions

  1. Run the real chat loop with a scripted provider returning two safe mocked calls in successive generations, both ID same-provider-id. Both execute and persist as distinct APPLIED rows.
  2. Pass the repair function a transcript containing generation 1's call/result and generation 2's unmatched same-ID call, plus the corresponding generation-scoped settled ledger rows.
live ledger: [(1, 'same-provider-id', 'APPLIED'), (2, 'same-provider-id', 'APPLIED')]
repair message count: 3
last message after repair: assistant tool_use id='reused'

The later call remains unpaired. Independently reproduced with synthetic provider messages and temporary databases, no live requests.

Impact and limits

A restart between accepted response and batch checkpoint can leave a transcript the provider rejects on resume, or associate the wrong generation's evidence. Unresolved external operations are still separately fenced; this is not evidence that unsafe tools replay automatically. A full crash/restart/provider integration was not staged.

Acceptance criteria

  • Pair repair by transcript occurrence/generation, or establish consistent unique IDs at acceptance and persist that mapping.
  • Preserve ledger correlation and native provider contracts.
  • Add a persisted two-generation/same-ID resume test at the unmatched-call boundary for applied, failed and effect-free operations.
  • Keep unresolved external-effect refusal intact.

No source changes were made.

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

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions