Skip to content

Stop unstarted tool calls after batch cancellation #17

Closed

Description

@TaewoooPark

Problem

When a model response contains multiple tool calls, aborting the first call does not stop the remaining calls in that response. A following write can still execute even though the final loop result is aborted.

This was reproduced offline using the actual core runLoop, actual CLI ToolExecutor, and a scripted model response on 0.3.1. It was not a live-model attempt to induce that exact sequence. The affected code is unchanged at main commit 01b49ad5a4d3e60395aa61f094275042a88aae0c.

Deterministic reproduction

Return both calls in one ScriptedTransport response:

[
  {"name":"bash","arguments":{"command":"sleep 5"}},
  {"name":"write","arguments":{"path":"batch-write-after-esc.txt","content":"batch continued after abort"}}
]

Use a disposable working directory and an AbortController. Abort approximately 100 ms after the first tool_start. With automatic execution, the recorded result was:

{"reason":"aborted","toolStarts":["bash","write"],"fileExists":true,"requests":1}

Separately, passing an already-aborted signal directly to ToolExecutor.run(write, signal) still created a file. Interactive approval may insert a new approval prompt, so the write observation above specifically uses automatic execution, not a claim that a fresh approval was bypassed.

Starting points

  • loop.ts: tool-call batch has no abort check between calls; the next check is at the next model turn.
  • ToolExecutor.run() has no initial abort guard, and write performs its side effect without consulting the signal.

Minimal scope and acceptance criteria

  • Once cancellation is observed, no remaining tool in the response starts, and no next model request is made.
  • Direct executor calls with an already-aborted signal refuse side effects before hooks, approval prompts, or command/file execution.
  • End with aborted and preserve coherent transcript/checkpoint state for the completed, interrupted, and unstarted tool calls. Do not mark an unstarted call as executed.
  • Add deterministic regressions for a two-call batch, an already-aborted executor, and a normal uninterrupted batch. Exercise both event order and the absence of a marker file.
  • Preserve normal completion and existing resume protections; do not weaken uncertain-operation handling to make the test pass.

No model API is required: use ScriptedTransport, a disposable fixture, and bounded cleanup. A focused core/executor patch is sufficient; a general scheduler rewrite is outside scope.

This differs from the terminal-cancellation issue (an existing process remains alive) and from #7 (checkpoint classification of an uncertain write).

Priority: P1. Not a good first issue: cancellation, tool batching, and durable transcript semantics intersect here.

Related work and coordination

Related process-lifetime fix: #16. Preserve the uncertain-operation resume protection tracked in #7.

Activity

  1. added
    bugSomething isn't working
    help wantedExtra attention is needed
    priority:P1Execution intent or state correctness; maintainer priority
    area:runtimeExecution, permissions, cancellation, journals, and resume
    on Sep 21, 2026
  2. TaewoooPark commented on Oct 4, 2026

    @TaewoooPark
    OwnerAuthor

    Resolved by #40. All five acceptance criteria have been independently verified and are now checked.

    The real core loop and CLI executor were exercised offline with a running bash followed by write in one scripted model response. After cancellation, the remaining write has no start/end execution events or durable mutation intent, receives an explicit not-executed observation, and causes no next model request. The completed transcript matches the checkpoint; resume retains it, and uncertain-operation protections remain intact. Cancellation after the final call also returns aborted before repair or loop detection.

    For historical accuracy, #37 had already added the direct-executor cancellation guard and prevented the original file-write side effect on current main. #40 preserves that guard and fixes the remaining batch-loop execution records and termination behavior. The already-aborted direct call was independently checked to refuse hooks, approval, and file changes.

    The four submitted regressions and nine additional independent scenarios passed. Current main plus the exact PR patch passed all 1,643 local tests, and all five CI jobs passed: https://github.com/TaewoooPark/Motifcode/actions/runs/37100770804 . Validation used scripted responses and disposable fixtures; no live Infron/Motif model call was made.

    Thanks @adity982 for the focused contribution. This records the source fix in main; an npm release is separate.

    Merged commit: 54f9d5b . Post-merge main CI also passed all five jobs: https://github.com/TaewoooPark/Motifcode/actions/runs/37180040334 .

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

    area:runtimeExecution, permissions, cancellation, journals, and resumebugSomething isn't workinghelp wantedExtra attention is neededpriority:P1Execution intent or state correctness; maintainer priority

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions