mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Update stall docs to use > semantics, skip checkpoint-state tests, simplify NextSpeaker fallback test
Agent-Logs-Url: https://github.com/microsoft/agent-framework/sessions/cc9ea5a8-84d8-4b6d-bb60-ac9619824d81 Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
964210730c
commit
4dbe3598db
+20
-23
@@ -79,8 +79,7 @@ Assessment:
|
||||
- Correctly matches Python's `stall_count > max_stall_count` behavior.
|
||||
- `MaxStallCount` should now be read as "number of stalls tolerated before reset".
|
||||
- Test setups that need reset on the first stalled ledger use `WithMaxStalls(0)`.
|
||||
- The original test plan text still describes `StallCount >= MaxStallCount`; that part of the plan
|
||||
is stale relative to the chosen cross-language behavior.
|
||||
- The original test plan text has been updated to use `StallCount > MaxStallCount`.
|
||||
|
||||
### Stall-triggered plan reviews preserve the stalled flag
|
||||
|
||||
@@ -212,11 +211,11 @@ Summary: **2 complete, 1 mostly complete / 3 planned**.
|
||||
|
||||
| Planned Test | Current Status | Notes |
|
||||
|---|---|---|
|
||||
| `Checkpoint_Saves_TaskContext` | Not implemented | No direct assertion on serialized `MagenticTaskContext`. |
|
||||
| `Checkpoint_Saves_TaskContext` | Intentionally skipped | Serialized checkpoint format is an internal detail; tested behaviorally instead. |
|
||||
| `Checkpoint_Resume_Continues_Correctly` | Behaviorally covered | Resume is exercised by approval, revision, multiple revision, and stall-with-signoff scenarios. |
|
||||
| `Checkpoint_Preserves_ProgressLedger` | Not implemented | No direct assertion that progress-ledger state survives checkpoint/restore. |
|
||||
| `Checkpoint_Preserves_ProgressLedger` | Intentionally skipped | Serialized checkpoint format is an internal detail; tested behaviorally instead. |
|
||||
|
||||
Summary: **1 behaviorally covered, 2 not implemented / 3 planned**.
|
||||
Summary: **1 behaviorally covered, 2 intentionally skipped / 3 planned**.
|
||||
|
||||
### 9. Edge Cases
|
||||
|
||||
@@ -242,23 +241,20 @@ Summary: **1 behaviorally covered, 1 partial, 2 not implemented / 4 planned**.
|
||||
|
||||
## Review Findings and Recommended Follow-Up
|
||||
|
||||
1. **Fix stale setup in `NextSpeaker_Empty_Falls_Back_To_First`.** The test still supplies
|
||||
`factsResponse2` and `planResponse2` after the first progress ledger. Since normal agent return
|
||||
no longer replans, those responses are consumed as invalid progress-ledger retry attempts before
|
||||
the valid satisfied ledger. The test should be simplified to match the updated control flow:
|
||||
initial facts, initial plan, empty-speaker ledger, satisfied ledger, final answer.
|
||||
1. **~~Fix stale setup in `NextSpeaker_Empty_Falls_Back_To_First`.~~** ✅ Addressed. The stale
|
||||
`factsResponse2` and `planResponse2` have been removed. The test now matches the updated control
|
||||
flow: initial facts, initial plan, empty-speaker ledger, satisfied ledger, final answer.
|
||||
|
||||
2. **Clean up stale comments that describe the old stall threshold.** A few comments still describe
|
||||
`MaxStallCount=1` or "reaches threshold" in old `>=` terms. The code and test setup now use
|
||||
`>` semantics correctly, but comments should consistently say `StallCount > MaxStallCount`.
|
||||
2. **~~Clean up stale comments that describe the old stall threshold.~~** ✅ Addressed. Comments
|
||||
now consistently use `StallCount > MaxStallCount` and `>` semantics.
|
||||
|
||||
3. **Update the original test plan if it remains a living document.** The plan currently states
|
||||
`IsStalled (StallCount >= MaxStallCount)`. The implemented and cross-language-aligned behavior is
|
||||
now `StallCount > MaxStallCount`.
|
||||
3. **~~Update the original test plan if it remains a living document.~~** ✅ Addressed. The plan
|
||||
now uses `StallCount > MaxStallCount` throughout.
|
||||
|
||||
4. **Add direct checkpoint-state assertions if stable infrastructure exists.** Current tests prove
|
||||
resume behavior works but do not inspect serialized `MagenticTaskContext` or progress-ledger
|
||||
state.
|
||||
4. **Skip direct checkpoint-state assertions.** The serialized checkpoint format is an internal
|
||||
implementation detail. Current tests prove resume behavior works through plan-review workflows
|
||||
that pause and resume across checkpoint boundaries. Direct payload inspection is intentionally
|
||||
omitted.
|
||||
|
||||
5. **Strengthen instruction-delivery verification.** `Instruction_Message_Sent_When_Present`
|
||||
currently proves the flow completes with an instruction present. A stronger test would directly
|
||||
@@ -271,7 +267,8 @@ Summary: **1 behaviorally covered, 1 partial, 2 not implemented / 4 planned**.
|
||||
|
||||
The current Magentic E2E suite is a strong implementation of the original plan. It covers
|
||||
**21 fully built workflow tests** and includes important production fixes that align .NET with the
|
||||
Python Magentic orchestration model. The most important remaining work is cleanup and precision:
|
||||
remove stale response setup from one fallback test, align comments and the original plan text with
|
||||
the `>` stall threshold, and add direct state/edge-case tests if those behaviors need stronger
|
||||
contract coverage.
|
||||
Python Magentic orchestration model. The addressed follow-up items include: stale test setup
|
||||
cleanup in the empty-speaker fallback test, consistent `StallCount > MaxStallCount` comments and
|
||||
plan text, and an explicit decision to skip direct checkpoint-state inspection in favor of
|
||||
behavioral coverage. The remaining optional work is edge-case tests (zero participants,
|
||||
post-termination rejection) and stronger instruction-delivery verification.
|
||||
|
||||
@@ -32,7 +32,7 @@ DelegateToTeamAsync
|
||||
├─> CHECK: IsRequestSatisfied? → PrepareFinalAnswerAsync
|
||||
├─> CHECK: IsInLoop OR !IsProgressBeingMade? → Increment StallCount
|
||||
│ └─> Else: Decrement StallCount (min 0)
|
||||
├─> CHECK: IsStalled (StallCount >= MaxStallCount)? → ResetAndReplanAsync
|
||||
├─> CHECK: IsStalled (StallCount > MaxStallCount)? → ResetAndReplanAsync
|
||||
├─> Validate NextSpeaker → Fallback to first participant if empty
|
||||
├─> CHECK: Invalid NextSpeaker? → Warning + PrepareFinalAnswerAsync
|
||||
└─> Send instruction + TurnToken to next agent
|
||||
@@ -70,7 +70,7 @@ ResetAndReplanAsync
|
||||
|-----------|-------------|-------------------|
|
||||
| `MaxRoundLimit_Terminates_Workflow` | RoundCount exceeds MaxRoundCount | Workflow terminates with "maximum round count limit" message |
|
||||
| `MaxResetLimit_Terminates_Workflow` | ResetCount exceeds MaxResetCount | Workflow terminates with "maximum reset count limit" message |
|
||||
| `MaxStallCount_Triggers_Reset` | StallCount reaches MaxStallCount | ResetAndReplanAsync called, ResetChatSignal sent |
|
||||
| `MaxStallCount_Triggers_Reset` | StallCount exceeds MaxStallCount | ResetAndReplanAsync called, ResetChatSignal sent |
|
||||
|
||||
### 4. Stall Detection Tests
|
||||
|
||||
@@ -79,7 +79,7 @@ ResetAndReplanAsync
|
||||
| `Stall_IsInLoop_Increments_StallCount` | ProgressLedger reports IsInLoop=true | StallCount incremented |
|
||||
| `Stall_NoProgress_Increments_StallCount` | ProgressLedger reports IsProgressBeingMade=false | StallCount incremented |
|
||||
| `Progress_Made_Decrements_StallCount` | ProgressLedger reports progress being made | StallCount decremented (min 0) |
|
||||
| `Consecutive_Stalls_Trigger_Reset` | Multiple stalls in a row reach MaxStallCount | Reset and replan triggered |
|
||||
| `Consecutive_Stalls_Trigger_Reset` | Multiple stalls in a row exceed MaxStallCount | Reset and replan triggered |
|
||||
|
||||
### 5. Progress Ledger Validation Tests
|
||||
|
||||
@@ -107,11 +107,16 @@ ResetAndReplanAsync
|
||||
|
||||
### 8. Checkpoint/Resume Tests
|
||||
|
||||
> **Note:** Direct checkpoint-state inspection tests (`Checkpoint_Saves_TaskContext`,
|
||||
> `Checkpoint_Preserves_ProgressLedger`) are **skipped** — the serialized checkpoint format is an
|
||||
> internal implementation detail. Checkpoint resume is instead verified behaviorally through
|
||||
> plan-review workflows that pause and resume across checkpoint boundaries.
|
||||
|
||||
| Test Name | Description | Expected Behavior |
|
||||
|-----------|-------------|-------------------|
|
||||
| `Checkpoint_Saves_TaskContext` | Workflow checkpointed mid-execution | MagenticTaskContext state serialized |
|
||||
| `Checkpoint_Resume_Continues_Correctly` | Resume from checkpoint | TaskContext restored, execution continues from saved state |
|
||||
| `Checkpoint_Preserves_ProgressLedger` | Resume preserves ledger state | ProgressLedger state correctly restored |
|
||||
| `Checkpoint_Saves_TaskContext` | ~~Workflow checkpointed mid-execution~~ | *Skipped — internal format* |
|
||||
| `Checkpoint_Resume_Continues_Correctly` | Resume from checkpoint | TaskContext restored, execution continues from saved state (behaviorally covered by plan-review tests) |
|
||||
| `Checkpoint_Preserves_ProgressLedger` | ~~Resume preserves ledger state~~ | *Skipped — internal format* |
|
||||
|
||||
### 9. Edge Cases
|
||||
|
||||
|
||||
@@ -264,7 +264,8 @@ public class MagenticOrchestrationTests
|
||||
{
|
||||
// Arrange: First progress ledger returns empty next_speaker, which should fall back to first participant.
|
||||
// Round 1: empty speaker → fallback to Worker (first participant) → Worker echoes
|
||||
// Round 2 (after Worker responds, TakeTurnAsync re-enters): new plan + satisfied ledger → final answer
|
||||
// Round 2 (after Worker responds): RunCoordinationRoundAsync → satisfied ledger → final answer
|
||||
// Note: No replan on agent return — only progress ledger is created on subsequent turns.
|
||||
List<ChatMessage> factsResponse1 = CreatePlanResponse("Facts about the task");
|
||||
List<ChatMessage> planResponse1 = CreatePlanResponse("Step 1: Execute");
|
||||
List<ChatMessage> emptyNextSpeakerLedger = CreateProgressLedgerResponse(
|
||||
@@ -274,9 +275,7 @@ public class MagenticOrchestrationTests
|
||||
nextSpeaker: "", // Empty - should fall back to first participant
|
||||
instructionOrQuestion: "Please help with this task");
|
||||
|
||||
// Round 2 responses (after Worker echoes back, orchestrator re-enters TakeTurnAsync → UpdatePlanAndDelegateAsync)
|
||||
List<ChatMessage> factsResponse2 = CreatePlanResponse("Updated facts");
|
||||
List<ChatMessage> planResponse2 = CreatePlanResponse("Updated plan");
|
||||
// Round 2: satisfied ledger + final answer (no replan on normal agent return)
|
||||
List<ChatMessage> satisfiedLedger = CreateProgressLedgerResponse(
|
||||
isRequestSatisfied: true,
|
||||
isInLoop: false,
|
||||
@@ -287,7 +286,7 @@ public class MagenticOrchestrationTests
|
||||
|
||||
TestReplayAgent manager = new(
|
||||
[factsResponse1, planResponse1, emptyNextSpeakerLedger,
|
||||
factsResponse2, planResponse2, satisfiedLedger, finalAnswerResponse],
|
||||
satisfiedLedger, finalAnswerResponse],
|
||||
name: "Manager");
|
||||
TestEchoAgent worker = new(name: "Worker");
|
||||
|
||||
@@ -677,8 +676,8 @@ public class MagenticOrchestrationTests
|
||||
[Fact]
|
||||
public async Task MaxResetLimit_Terminates_Workflow()
|
||||
{
|
||||
// Arrange: MaxStallCount=1, MaxResets=1.
|
||||
// Flow: facts1, plan1 → ledger1(stall: isInLoop=true) → StallCount=1 → IsStalled → ResetAndReplanAsync
|
||||
// Arrange: MaxStallCount=0, MaxResets=1.
|
||||
// Flow: facts1, plan1 → ledger1(stall: isInLoop=true) → StallCount=1 > 0 → IsStalled → ResetAndReplanAsync
|
||||
// → ResetCount becomes 1 → facts2, plan2 → DelegateToTeamAsync
|
||||
// → RunCoordinationRoundAsync: CheckLimits() detects ResetCount(1) >= MaxResetCount(1) → terminates
|
||||
List<ChatMessage> factsResponse1 = CreatePlanResponse("Initial facts");
|
||||
|
||||
Reference in New Issue
Block a user