Skip to content

.NET: Persist ForeachExecutor iteration state across checkpoints#6051

Merged
peibekwe merged 1 commit into
mainfrom
peibekwe/declarative-bugfix
May 26, 2026
Merged

.NET: Persist ForeachExecutor iteration state across checkpoints#6051
peibekwe merged 1 commit into
mainfrom
peibekwe/declarative-bugfix

Conversation

@peibekwe
Copy link
Copy Markdown
Contributor

@peibekwe peibekwe commented May 23, 2026

Motivation and Context

ForeachExecutor kept _index, _values, and HasValue in private fields that the checkpoint pipeline does not persist. When a workflow checkpoints inside a Foreach body (e.g. while a Question awaits input) and resumes in a new process, ExecutorBinding.CreateInstanceAsync rebuilds the executor with defaults on the next TakeNextAsync, so the loop exits after iteration 1.

Fixes #5009

Description

Override OnCheckpointingAsync and OnCheckpointRestoredAsync to track these values during checkpointing and rehydrate on resume.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

@peibekwe peibekwe self-assigned this May 23, 2026
Copilot AI review requested due to automatic review settings May 23, 2026 00:55
@moonbox3 moonbox3 added .NET workflows Related to Workflows in agent-framework labels May 23, 2026
@github-actions github-actions Bot changed the title Persist ForeachExecutor iteration state across checkpoints .NET: Persist ForeachExecutor iteration state across checkpoints May 23, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a .NET declarative workflow bug where ForeachExecutor lost its in-memory iteration cursor across checkpoint/restore (e.g., pausing inside the loop body and resuming in a new process), causing the loop to terminate after the first iteration.

Changes:

  • Persist ForeachExecutor iteration state (_index, _values, HasValue) during checkpointing and rehydrate it on checkpoint restore.
  • Add regression/unit tests covering checkpoint restore behavior, including empty sources and restoring with no saved state.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/ForeachExecutor.cs Adds checkpoint save/restore hooks to persist and restore foreach iteration state.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/ForeachExecutorTest.cs Adds regression tests and an in-memory workflow context to exercise checkpointing behavior.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 89% | Result: All clear

Reviewed: Correctness, Security Reliability, Test Coverage, Design Approach


Automated review by peibekwe's agents

@peibekwe peibekwe marked this pull request as ready for review May 26, 2026 16:46
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 4 | Confidence: 90%

✓ Correctness

The implementation correctly persists and restores ForeachExecutor iteration state across checkpoints. The accessibility modifiers are correct (protected override of protected internal virtual from a different assembly without InternalsVisibleTo), the base call ordering follows existing patterns, the PortableValue roundtrip logic handles all expected value types including blanks, and the null guard on savedValues correctly handles the first-run/no-checkpoint case. The state keys are scoped to the executor via BindWorkflowContext(executor.Id) in the runtime, avoiding collision risk.

✓ Security Reliability

The checkpoint persistence implementation is well-designed from a security and reliability perspective. State is properly scoped per-executor via BoundWorkflowContext, null-safety is maintained through the savedValues gate, cross-assembly accessibility modifiers are correct, and base class calls are properly ordered. No security vulnerabilities (injection, secrets, unsafe deserialization) or significant reliability concerns were identified.

✓ Test Coverage

The new tests provide good coverage for the checkpoint/restore regression fix (GH-5009). Three test cases cover the core regression scenario (multi-item loop restored mid-iteration), the edge case of restoring with no saved state, and the edge case of an empty values array. The assertions on HasValue are meaningful since they directly verify the broken behavior (premature loop exit). One minor gap: no test verifies that the actual iterated VALUES survive the PortableValue roundtrip correctly — only the cursor position and HasValue flag are checked.

✗ Design Approach

The checkpoint persistence added here fixes the interpreted declarative ForeachExecutor, but it does not cover the source-generated foreach path that uses the same private cursor fields and the same HasValue-based routing. That leaves a class of checkpoint/resume scenarios still broken, so I would request changes before merging.

Flagged Issues

  • The PR fixes only the interpreted ObjectModel/ForeachExecutor, but source-generated foreach executors still keep _index, _values, and HasValue in private fields (CodeGen/ForeachTemplate.cs:39-42) and route on HasValue after TakeNextAsync (Interpreter/WorkflowTemplateVisitor.cs:147-160). Because generated actions inherit Executor through ActionExecutor<TMessage> (Kit/ActionExecutor.cs:54-64) and participate in the same checkpoint lifecycle, a source-generated workflow that checkpoints inside a foreach body will still lose its iteration cursor on resume.

Automated review by peibekwe's agents

@peibekwe peibekwe added this pull request to the merge queue May 26, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks May 26, 2026
@peibekwe peibekwe added this pull request to the merge queue May 26, 2026
Merged via the queue into main with commit 08697f8 May 26, 2026
41 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET workflows Related to Workflows in agent-framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET: [Bug]: ForeachExecutor loses iteration state (_index, _values) after checkpoint restore — loop exits immediately on resume

5 participants