Repository navigation
[AI-1439] Await import background tasks; de-flake retry timing tests - #360
Conversation
ImportCommand.HandleImport computed `ranBackground = backgroundTasks.IsEmpty`
(inverted, missing a `!`). With background title/summary work present, the
await block was skipped, so `Task.WhenAll(backgroundTasks)` never ran: the
method returned and disposed the shared `SemaphoreSlim(3)` while a title task
could still be running, whose `finally { Release(); }` then threw
ObjectDisposedException (the intermittent unit-test failures). The same
inverted flag also dropped the Titles/Summaries rows from the Done summary.
- Fix: `!backgroundTasks.IsEmpty`, renamed to `hasBackgroundWork` and extracted
into a named `HadBackgroundWork` seam so the decision is unit-tested and can't
silently re-invert (mirrors the ComputePerSourceFinalCounts convention).
- Regression tests: HadBackgroundWork empty/non-empty; WriteDoneGrid renders the
Titles/Summaries rows iff RanBackground.
- Widen the two SendWithRetry wall-clock timing tests so scheduler jitter on
loaded CI runners can't flake them (total 1200ms->5000ms; elapsed bound
2000ms->5000ms). Both invariants preserved.
Port-contention flakes (LocalPermissionBridge / orchestrator tests) are already
covered by the production ephemeral-port + retry bind (#340) plus
[NotInParallel] grouping; no test change needed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoAwait import background tasks and harden SendWithRetry timing tests
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
Context used 1.
|
- Trim the historical/CI-rationale narratives from HadBackgroundWork's XML doc and the ImportBackgroundWork / retry / display-grid test comments down to concise intent (repo comment-conciseness guideline). - SendWithRetry budget-exhausted test: instead of a 5s totalTimeout (which the never-completing send ran to the full budget, ~5s/run), use a small per-attempt cap (50ms) under a modest 1500ms total so retries accrue fast — `attempts > 1` stays robust and the test finishes in ~1.5s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed both Qodo findings in fd4d109:
Also for the record: the first ubuntu run failed on |
Fixes two independent sources of kcap-cli unit-test flakiness surfaced while working #335. Linear: AI-1439.
Part 1 — production bug:
kcap importnever awaited background title/summary workImportCommand.HandleImportcomputed the background-phase guard inverted:The one boolean gates (a) the
if (…)block thatawait Task.WhenAll(backgroundTasks), and (b) the two Done-summary guards that print the Titles/Summaries rows. Because it was inverted, an import that has background work (title generation, or summaries under--generate-summaries):using var concurrencyLimit = new SemaphoreSlim(3)was disposed → the straggler'sfinally { concurrencyLimit.Release(); }threwObjectDisposedException(the intermittent unit failure — rare only because tasks usually finish before the method returns);Introduced in
fa2f013(2026-04-26); #335's new tests merely exercisedHandleImportenough to surface it.Fix:
!backgroundTasks.IsEmpty, renamed tohasBackgroundWorkand extracted into a namedHadBackgroundWorkseam so the decision is unit-testable and can't silently re-invert — mirroring the existingComputePerSourceFinalCounts"extract-and-test-the-helper-directly" convention in this file. The twoRanBackground: falseearly-exit constructions stay correct.This changes
kcap import's observable Done-summary output (the Titles/Summaries rows now appear when there was background work) — intended.Part 2 — parallel-load timing flakes
SendWithRetrytiming tests (the genuine remaining flaky-failure vector): widen the wall-clock margins while preserving each invariant.…_after_budget_exhausted:totalTimeout1200 ms → 5000 ms, so a scheduler stall during attempt 1 can't consume the whole budget before attempt 2 begins (attempts > 1is itself condition-based).…_enforces_total_timeout_…: elapsed upper bound 2000 ms → 5000 ms (still ≪ the 30 s per-attempt cap it guards against).LocalPermissionBridgeTests,AgentOrchestratorReviewerTokenTests,AgentOrchestratorVendorTests): already fixed at the production level —LocalPermissionBridgebinds an OS-assigned ephemeral loopback port behind a process-global claim set + 15-attempt transient-bind retry (Fix flaky LocalPermissionBridge bind under parallel tests (jittered backoff) #340), and the bridge tests carry[NotInParallel]grouping. No test change needed.AcpTranscriptAggregationTests/AgentOrchestratorVendorTestsTask.Delay(200/100): these are "settle, then assert a negative" waits — a short wait risks a false pass (missed regression), not a flaky fail — so they're not the parallel-failure vector and are left as-is.Tests
ImportBackgroundWorkTests:HadBackgroundWorkempty → false, non-empty → true.ImportDisplayGridTests:WriteDoneGridrenders the Titles/Summaries rows iffRanBackground.HttpClientExtensionsRetryTestsmargins.Verified locally: full Import namespace (234),
SetupCommandTests/ImportVisibilityTests(26, driveHandleImport), andHttpClientExtensionsRetryTests(5) all pass; build clean;check-linear-ids.shpasses.Out of scope / follow-ups
🤖 Generated with Claude Code