fix(flow): reset cancellation flag at the start of each execution - #97
fix(flow): reset cancellation flag at the start of each execution#97XuQuanxin04 wants to merge 2 commits into
Conversation
cancel() set self._cancelled = True, but that flag was only ever read in _execute() and never reset. Because run(), resume() and rerun_step() all share the same _execute() path on a LightFlow instance, a single cancel() call permanently poisoned the instance: every subsequent run skipped all of its steps with "cancelled before execution", even when cancellation was requested after a run had already finished. Reset the flag when a new execution begins. In-flight cancellation is unaffected because the flag is still re-checked before every step. Co-Authored-By: Claude <noreply@anthropic.com>
wxai-space
left a comment
There was a problem hiding this comment.
Changes requested
The underlying bug is valid, but resetting self._cancelled = False at the start of _execute() introduces a cancellation race.
If run() has been called but has not entered _execute() yet, a concurrent cancel() can set the flag and _execute() immediately clears it. The run then executes successfully instead of honoring the cancellation request.
Please replace the shared sticky boolean with an execution-scoped cancellation token or generation captured when run(), resume(), or rerun_step() starts. The token should be checked before each step, and cancellation after an execution has completed should be a no-op for the next run.
Please add a deterministic regression test covering cancellation between invocation of run() and entry into _execute(), plus a test documenting behavior for concurrent executions.
|
Thanks for the review. I replaced the sticky shared boolean with execution-scoped cancellation events registered before
Added deterministic coverage for:
The public behavior and the recommendation to use separate Validation: Updated in |
|
Addressed in commit 288d692: cancellation is now execution-scoped, covers the pre-_execute race and concurrent executions, and is documented. Full suite: 265 passed, 1 skipped. I also posted the detailed validation on PR #97.
At 2026-09-04 16:21:29, "weego" ***@***.***> wrote:
@wxai-space requested changes on this pull request.
Changes requested
The underlying bug is valid, but resetting self._cancelled = False at the start of _execute() introduces a cancellation race.
If run() has been called but has not entered _execute() yet, a concurrent cancel() can set the flag and _execute() immediately clears it. The run then executes successfully instead of honoring the cancellation request.
Please replace the shared sticky boolean with an execution-scoped cancellation token or generation captured when run(), resume(), or rerun_step() starts. The token should be checked before each step, and cancellation after an execution has completed should be a no-op for the next run.
Please add a deterministic regression test covering cancellation between invocation of run() and entry into _execute(), plus a test documenting behavior for concurrent executions.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS and Android. Download it today!
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Summary
LightFlow.cancel()setsself._cancelled = True, but that flag was only read in_execute()and never reset. Becauserun(),resume(), andrerun_step()all go through the same_execute()path on a reusableLightFlowinstance, a singlecancel()call permanently poisoned the instance — every subsequent run skipped all of its steps with"cancelled before execution", even when cancellation was requested after a run had already finished.Reproduction
Fix
Reset
self._cancelled = Falseat the start of_execute(). In-flight cancellation is unaffected — the flag is still re-checked before every step, so acancel()from another thread during a run still skips the remaining steps of that run.Compatibility
agent.run("hello")behavior is unchanged.stream=Truebehavior is unchanged.cancel()behavior is preserved (added a test for it).Tests
python -m compileall -q LightAgentPYTHONPATH=. python -m pytest -q tests/test_v065_core.py tests/test_v070_tracing.py tests/test_memory_policy.py— 57 passedtests/test_lightflow.py:test_lightflow_cancel_during_run_skips_remaining_steps_of_that_run— guards the existing in-flight cancel behavior.test_lightflow_cancel_between_runs_does_not_poison_the_next_run— regression test for this bug.tests/test_lightflow.py: 16 passed.