Skip to content

Fix system-message response test subscription races - #2638

Draft
devm33 wants to merge 2 commits into
mainfrom
devm33/deflake-preamble-response-subscription
Draft

Fix system-message response test subscription races#2638
devm33 wants to merge 2 commits into
mainfrom
devm33/deflake-preamble-response-subscription

Conversation

@devm33

@devm33 devm33 commented Sep 12, 2026

Copy link
Copy Markdown
Member

Summary

Fix two test-only lost-idle races: SystemMessageSectionsE2ETests.Should_Use_Replaced_Preamble_Section_In_Response and SessionE2ETests.Should_Create_A_Session_With_Replaced_SystemMessage_Config. Both old scenarios awaited SendAsync before subscribing through GetFinalAssistantMessageAsync. A fast turn can dispatch its assistant messages and ephemeral session.idle before that subscription; history can recover assistant content but not idle, leaving the helper waiting until its deadline.

Use the existing public SendAndWaitAsync in each response scenario so subscription precedes sending. Preserve final-not-intermediate response semantics and both explicit 120-second E2E budgets. The preamble's Botanica gardening configuration, Who are you? prompt, and response assertions remain unchanged. The replaced-message case retains its replacement configuration, What is your full name? prompt, NotNull/Testy/not-GitHub response assertions, nonempty actual exchanges, and exact wire-system-message equality.

Two deterministic real-SDK loopback JSON-RPC regressions in ClientSessionLifetimeTests call those same E2E response scenarios. Each sends an intermediate assistant, tool activity, final assistant and idle before the send reply. A later delivered event affirmatively fences dispatch without sleeps. History contains both assistant messages but no idle. The published preamble scenario/regression is byte-identical; the new commit adds only the accepted replaced-case repair. Cumulative scope is three test files, with no production SDK, shared TestHelper, framework, capture, deadline, retry, skip or concurrency change. The appended-message repair in #2636 remains separate and untouched.

Original motivating occurrences, both attempt 1 with consumed SDK f45c46fd1812f8bed5b4cbc250f47177c83068f0:

Case Runtime run / job Actual runtime checkout
Replaced preamble github/copilot-agent-runtime run 34694180678, job 103555442838, associated with github/copilot-agent-runtime#20370 Synthetic merge ba1cd6cbad8d0606d2c3d06b41506a512678f8c3
Replaced system message github/copilot-agent-runtime run 34694551018, job 103556281133, associated with github/copilot-agent-runtime#20299 Synthetic merge 42e1d40b7fa22a9fcc7740216a2a14c6c386db60

Each original leg reported 894 passed, 1 failed, 4 skipped, with Timeout waiting for assistant message at TestHelper.cs:77/85 (preamble caller line 65; replaced-message caller line 82). These are actual consumed checkouts, not assumed runtime PR heads.

The live test-observation races are deterministically proven; both original CI causes remain UNKNOWN. Neither occurrence retained an event/RPC trace proving its interleaving. The separate original SDK automatic timeout and runtime publication-permission boundary are preserved below; this repair is not claimed to fix that timeout.

Validation

  • Preserved standalone RED proof for each scenario: the forced early-idle regression failed with Timeout waiting for assistant message; idle drained before send reply: True; durable assistant messages: 2; durable idle events: 0, retaining the original helper stack. Each standalone GREEN passed 1/1, followed by its 126-case whole-unit validation. Those evidence seals remain unchanged.
  • Combined final source, published as 9f4b9e59ca7ce0516790ad18afc31f7bfd7ead01 with sole parent 7509bc154359e26b2aa55a0c6a92bda6ed6ecac6: whole ClientSessionLifetimeTests 127/127, with named pass records proving both new regressions executed.
  • Both complete E2E classes in one normal replay invocation: 40/40 (SessionE2ETests 38; SystemMessageSectionsE2ETests 2), including both modified cases, unchanged identity, and unchanged appended case. All response assertions and replaced-message actual wire equality executed. No completed-suite failures/skips/cleanup errors.
  • Formatting and verification covered all three test files; SDK .NET lint passed; full SDK source build passed netstandard2.0/net8.0/net10.0, zero warnings/errors.
  • Reused the read-only Linux x64 CLI at runtime ee6465da61c6b394e40d3b5a2de78a485d500f60. All 114 build-output digests were rechecked before/after validation. It was previously staged through the runtime's vendored Bazel and CLI build with documented build-cache reuse; no runtime rebuild or unknown bundle was used for combined validation.
  • .NET SDK 10.0.100, net8.0/runtime 8.0.22, normal shared TCP and CAPI replay. GITHUB_ACTIONS=true blocks live-CAPI fallback and snapshot writes; COPILOT_AUTO_UPDATE=false and CopilotSkipCliDownload=true preserve the supplied CLI. This is local Linux replay, not cross-platform/live-CI proof.
  • Initial missing-NuGet-assets and missing-tsx fixture failures are preserved. The latter affected all 40 selected cases before their bodies and emitted fixture-cleanup NullReferenceException diagnostics. Dependencies were restored only after those observed failures; no assertions/source/snapshots were weakened. Subsequent combined suites completed successfully.

The preamble evidence, separate 48-file replaced-candidate seal and 41-file combined-validation seal remain intact. No original 7509 automatic result, historical image-capacity proof, or old source-head proof transfers to the combined commit.

Final combined SDK head Independent full-workflow proof
9f4b9e59ca7ce0516790ad18afc31f7bfd7ead01 0/10 — pending

The PR remains draft pending at least ten qualifying independent final-head workflow passes and parent review. This publication dispatched, reran and cancelled no CI jobs. The existing release-based .NET workflow is separate from the unpublished fixed-runtime validation branch described below.

Generated by Copilot

Original CI failure and validation gates

The original automatic run 34696477539, attempt 1 on 7509bc154359e26b2aa55a0c6a92bda6ed6ecac6, did not pass. The macOS default/CAPI shard 1 exceeded its configured 20-minute execution limit and was cancelled. The downstream .NET required failure reflects that cancellation, not a second test failure. The other 26 configured .NET jobs succeeded, but those automatic results are not independent proof.

The failed shard's log endpoint returned 404 and the run has no diagnostic artifacts. Its exact test, completed case count, and cleanup outcome remain unknown. The annotation identifies the configured limit; the 25-minute start-to-end wall interval does not redefine it. This loop did not cancel or rerun the job, and does not claim the pending combined repair fixes this timeout.

The separate fixed-runtime validation branch is not published. GitHub rejected its normal push because the OAuth app lacks workflow scope. The validated local runtime commit 390a22f5f48cefee7bfc6687e93c2b071e469215 remains available, but no remote branch exists. Publishing it requires credentials authorized to modify workflows; no alternate-credential or API-write workaround was attempted.

The SDK's existing full .NET workflow remains usable for separate release-based coverage. Its first manual wave will wait for the combined system-message candidate's validation and final pushed SDK SHA. All qualifying proof counts remain 0/10; no historical image-fixture run or automatic check transfers.

Independent manual proof: first workflow did not pass

Workflow / platforms Run SDK commit Attempt / event Observed outcome
.NET SDK Tests / all 27 configured jobs across Ubuntu, macOS, Windows and Alpine ARM64 3402 / 34698446440 9f4b9e59ca7ce0516790ad18afc31f7bfd7ead01 1 / workflow_dispatch Cancelled: 26 jobs succeeded; macOS 2b-rpc-q-z exceeded the job execution limit. 0/10 qualifying full-workflow successes.

Job 103565867411 completed as cancelled on September 12, 2026, at 14:34:27 UTC. Its annotation confirms the 20-minute execution limit; its 25-minute wall interval is a separate measurement. The original job log returned 404, one fallback failed, and the run has no artifacts. The active build/test/cleanup phase, individual case and cause remain unknown. Further proof waves are held; no duplicate dispatch or job rerun was made.

Two original-attempt, source-matched q-z comparison jobs completed in 130 and 165 seconds, each with 109 passes and one skip. They are not proof credits or a tail-latency estimate, and do not justify a generic timeout increase. Existing #2531 changes the general limit alongside production FFI behavior, but no causal link to this failure is established.

The completed original logs were audited: 25 test commands, 27 framework completions, 6,531 passed and 37 skipped, with no unclassified count gaps in those logs. Both repaired E2Es passed all nine expected framework executions. Each new unit regression passed all five eligible framework executions; the existing NET8_0_OR_GREATER file guard excludes net472, correcting the earlier six-framework estimate. No affected test was skipped in an eligible execution. These partial passes do not make the cancelled workflow a passing run.

Completed jobs logged the exact SDK checkout. Runtime acquisition remains release-based CLI 1.0.84-5, not fixed-runtime-source proof. Release checksum enforcement, producer/consumer paths and runner cleanup are recorded; independent runtime binary hashes and globally leak-free cleanup are not claimed. The cancelled shard's execution population and cleanup have not been established.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7e3dcf84-04c4-4bb7-8a42-7ca5904f3cf7
@devm33 devm33 added post-to-slack Post PR to reviewers Slack channel deep-review Requested detailed code review skip-changelog No user-facing changelog entry required labels Sep 12, 2026
@github-actions

This comment has been minimized.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7e3dcf84-04c4-4bb7-8a42-7ca5904f3cf7
@devm33 devm33 changed the title Fix preamble response test event subscription race Fix system-message response test subscription races Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

SDK Consistency Review

Reviewed the changes in this PR against the authoritative file list from pull_request_read (get_files).

Files changed (all test-only, .NET):

  • dotnet/test/E2E/SessionE2ETests.cs
  • dotnet/test/E2E/SystemMessageSectionsE2ETests.cs
  • dotnet/test/Unit/ClientSessionLifetimeTests.cs

Findings: No SDK production/client code (dotnet/src/**) is modified by this PR — all changes are confined to test files. The PR refactors existing E2E tests to use the already-existing CopilotSession.SendAndWaitAsync API (subscribing before sending, to avoid missing the ephemeral session.idle event) and adds new unit tests asserting that a late-arriving session.idle event doesn't get dropped when the reply to session.send is delayed.

Since no public API was added, changed, or removed, there is no cross-language feature-parity gap to flag for Node.js, Python, Go, Java, or Rust. This PR is internal-to-.NET-tests and does not require mirroring in other SDKs.

No action needed — consistency is maintained.

Generated by SDK Consistency Review Agent for #2638 · copilot · sonnet50 · 16.6 AIC · ⌖ 12.1 AIC · ⊞ 8.3K ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deep-review Requested detailed code review post-to-slack Post PR to reviewers Slack channel skip-changelog No user-facing changelog entry required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant