Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 1, 2025

Make IReportingStep and IReportingTask completion idempotent

Issue Analysis

The error message Cannot complete step '333e03eb-984a-4bc1-8163-627f1206228c' with state 'CompletedWithError'. Only 'InProgress' steps can be completed. indicates that a step cannot be completed when it's already in a non-InProgress state. The issue required making state transitions idempotent.

Requirements (from agent instructions)

  • ✅ Allow valid transitions: InProgress → Completed or CompletedWithError (or CompletedWithWarning)
  • ✅ Make double completion a noop instead of throwing exception
  • ✅ ANY completion attempt on a terminal state is a noop (per feedback from @davidfowl)

Implementation Summary

Core Changes (Updated per @davidfowl feedback)

  • ✅ Modified PipelineActivityReporter.CompleteStepAsync to be fully idempotent:

    • If step is already in ANY terminal state, return immediately (noop) - no exceptions
    • Simplified logic: just check if state is not InProgress and return if so
  • ✅ Modified PipelineActivityReporter.CompleteTaskAsync to be fully idempotent:

    • If task is already in ANY terminal state, return immediately (noop) - no exceptions
    • Simplified logic: just check if state is not InProgress and return if so

Test Updates (Updated per @davidfowl feedback)

  • ✅ Updated all tests to verify noop behavior for all terminal state transitions
  • ✅ Added helper methods ClearActivities() and AssertNoActivitiesEmitted() to reduce code duplication
  • ✅ Replaced 13 instances of repeated pattern with helper method calls

Validation Results

  • ✅ All 57 tests in PublishingActivityReporterTests pass

Behavior Changes

Before: Attempting to complete a step/task that was already completed would throw an exception

After (per @davidfowl feedback):

  • Attempting to complete a step/task when it's already in ANY terminal state is now a complete noop
  • No exceptions are thrown regardless of which terminal state is being requested
  • The original state and completion text are retained
  • This is the simplest and most robust idempotent implementation

Fixes #12601

Original prompt

This section details on the original issue you should resolve

<issue_title>Make state changes on IReportingStep and IReportingTask idempotent</issue_title>
<issue_description>```
(Cannot complete step '333e03eb-984a-4bc1-8163-627f1206228c'
with state 'CompletedWithError'. Only 'InProgress' steps can be completed.) (Step 'build-frontend' failed: Docker build failed with exit code 1.)


<agent_instructions>There are only a few states but we want to make sure that transitions are valid:
- Inprogress to complete or complete with error
- Noop on double completion
- Fail if going from a terminal state (comlete or complete with error) to a non terminal state (that's a coding bug)

</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Make state changes on IReportingStep and IReportingTask idempotent Make IReportingStep and IReportingTask completion idempotent Nov 1, 2025
Copilot AI requested a review from davidfowl November 1, 2025 22:42
@davidfowl
Copy link
Member

@copilot transitioning from terminal state to a different terminal state is fine. Just noop.

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot transitioning from terminal state to a different terminal state is fine. Just noop.

Updated in c9582ef. All terminal state transitions are now a noop - the implementation simply returns early if the step/task is already in any terminal state, keeping the original state and completion text.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • aka.ms
    • Triggering command: /usr/local/bin/bicep build /tmp/aspireC3HzyJ/env.module.bicep --stdout (dns block)
    • Triggering command: /usr/local/bin/bicep build /tmp/aspireRt1h6y/teststorage.module.bicep --stdout (dns block)
    • Triggering command: /usr/local/bin/bicep build /tmp/aspireB1SyMe/teststorage.module.bicep --stdout (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@github-actions
Copy link
Contributor

github-actions bot commented Nov 1, 2025

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 12602

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 12602"

@davidfowl davidfowl marked this pull request as ready for review November 1, 2025 23:11
@davidfowl davidfowl requested a review from mitchdenny as a code owner November 1, 2025 23:11
Copilot AI review requested due to automatic review settings November 1, 2025 23:11
Copy link
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 modifies the PipelineActivityReporter to make task and step completion operations idempotent. Previously, attempting to complete an already-completed task or step would throw an InvalidOperationException. Now, subsequent completion attempts on terminal states are no-ops.

Key Changes

  • Changed exception-throwing behavior to early returns when tasks/steps are already in terminal states (Completed, CompletedWithWarning, CompletedWithError)
  • Updated test suite to verify idempotent behavior instead of testing for exceptions
  • Added comprehensive test coverage for all terminal state transitions

Reviewed Changes

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

File Description
src/Aspire.Hosting/Pipelines/PipelineActivityReporter.cs Changed CompleteStepAsync and CompleteTaskAsync to return early when already in terminal state instead of throwing exceptions
tests/Aspire.Hosting.Tests/Publishing/PipelineActivityReporterTests.cs Replaced exception-testing tests with idempotency tests and added extensive coverage for terminal state transitions

await task.CompleteAsync(null, cancellationToken: CancellationToken.None);

// Assert - No new activity should be emitted (noop)
Assert.False(reporter.ActivityItemUpdated.Reader.TryRead(out _));
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

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

[nitpick] The pattern while (reporter.ActivityItemUpdated.Reader.TryRead(out _)) { } followed by Assert.False(reporter.ActivityItemUpdated.Reader.TryRead(out _)) is repeated throughout the new tests. Consider extracting this into a helper method like AssertNoActivitiesEmitted(reporter) to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => task.CompleteAsync(null, cancellationToken: CancellationToken.None));
// Clear activities
while (reporter.ActivityItemUpdated.Reader.TryRead(out _)) { }
Copy link

Copilot AI Nov 1, 2025

Choose a reason for hiding this comment

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

[nitpick] The activity clearing pattern while (reporter.ActivityItemUpdated.Reader.TryRead(out _)) { } is repeated 10+ times across the new tests. Consider creating a helper method like ClearActivities(reporter) to improve code maintainability and readability.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in dea6fa8. Added ClearActivities() and AssertNoActivitiesEmitted() helper methods and replaced all 13 instances of the duplicated patterns.

@davidfowl davidfowl merged commit ba652c4 into main Nov 2, 2025
300 of 302 checks passed
@davidfowl davidfowl deleted the copilot/make-state-changes-idempotent branch November 2, 2025 17:53
@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Nov 2, 2025
@davidfowl
Copy link
Member

/backport to release/13.0

@github-actions
Copy link
Contributor

github-actions bot commented Nov 2, 2025

Started backporting to release/13.0: https://github.com/dotnet/aspire/actions/runs/19015985829

@github-actions github-actions bot locked and limited conversation to collaborators Dec 3, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make state changes on IReportingStep and IReportingTask idempotent

3 participants