Skip to content

fix(test): keep provider teardown deadline tests from wedging their shard - #139090

Merged
steipete merged 1 commit into
mainfrom
fix/flaky-ui-e2e
Sep 5, 2026
Merged

steipete merged 1 commit into
mainfrom
fix/flaky-ui-e2e

Conversation

@steipete

@steipete steipete commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

src/gateway/worker-environments/provider-destroy-timeout.test.ts ("allows provider-owned checkpointing beyond five minutes during destroy/bootstrap-failure") failed four times on main CI today (runs 33959274889, 33960371295, 33961192294, 33962562919), each time as a 120s test timeout followed by a 180s hook timeout that wedged the whole shard.

Why This Change Was Made

Two lifetime defects amplified any slow turn into a shard wedge:

  1. The provider mock awaited an unresolved finish.promise whose only release was the test body's finally. Vitest's watchdog aborts the test context but does not unwind a suspended async body, so a timed-out test kept the provider held — and afterEach's service.stop() correctly joined it while fake timers were still installed, hanging the hook too.
  2. await started.promise waited for mock readiness without advancing fake timers, so any chain progress that requires a timer turn deadlocks permanently.

The fix awaits mock readiness through the existing timer-aware waitForFast, races held mocks against the test cancellation signal (racePromiseWithAbortSignal), and restores real timers before draining operations and before service.stop() in the shared fixture. All assertions and the five-minute checkpointing semantics are unchanged; production code is untouched.

User Impact

No user-facing change — test robustness only. Main CI stops losing the checks-node-compact-large-1 shard to this wedge.

Evidence

  • Synthetic fault injection reproduces the exact CI signature on the original code (test timeout + hook timeout) and passes with the fix: a zero-delay timer inserted before mock readiness deadlocks the original and passes with waitForFast; a suspended test cancellation wedges the original hooks and unwinds cleanly with the patch.
  • 30/30 local file invocations pass (300 cases), including five under extra CPU load.
  • Linux CI-shard replay on Blacksmith Testbox (tbx_01m1rr5m48gzwx01nhhk44es9w): the exact gateway-core-3 file set, 182 files / 3,397 tests passed.
  • Honest limit: the initiating CI stall was not reproduced naturally, so this is hardening against the observed failure mode, not a proven root-cause elimination. The synthetic probes cover precisely the two mechanisms that produce the observed signature. If the stall recurs, the follow-up is phase-boundary instrumentation in the non-isolated worker (recorded in the investigation notes).

Checklist

  • Tests updated/added
  • No production code changed

…hard

The five-minute checkpointing tests held the provider mock on an
unresolved promise and awaited mock readiness without advancing fake
timers. A watchdog timeout then left the provider held, and afterEach
joined it via service.stop() while fake timers were still installed,
turning one slow test into a test-timeout plus hook-timeout pair
(seen failing repeatedly on main CI).

Await mock readiness through the existing timer-aware waitForFast, race
held mocks against the test cancellation signal, and restore real timers
before draining operations and before service.stop() in the shared
fixture. Assertions and production code are unchanged.
@clawsweeper

clawsweeper Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: XS maintainer Maintainer-authored PR labels Sep 5, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 5, 2026
@clawsweeper

clawsweeper Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 5, 2026, 9:02 AM ET / 13:02 UTC.

ClawSweeper review

What this changes

Makes worker teardown tests release held provider mocks when cancelled and restore real timers before fixture shutdown.

Merge readiness

⛔ Blocked before merge - 3 items remain

Keep open: the test-lifecycle hardening remains absent from main and preserves meaningful deadline coverage. No concrete introduced defect was found; direct verification of the pinned Vitest contract remains incomplete.

Priority: P2
Reviewed head: bdfedd8e1c47a5c4fa4336e7668570e57a2daf89

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) Focused, assertion-preserving test hardening with reported fault-injection and shard validation; no concrete patch defect found.
Proof confidence 🌊 off-meta tidepool Not applicable: The supplied MEMBER classification exempts this test-only PR from contributor runtime proof; its reported fault probes and shard replay address fixture behavior without claiming a production fix.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: The supplied MEMBER classification exempts this test-only PR from contributor runtime proof; its reported fault probes and shard replay address fixture behavior without claiming a production fix.
Evidence reviewed 10 items Verified introduced change and merge result: The pinned merge-base-to-head change contains only test and test-support edits. Raw test-merge parents match pinned main followed by the original head, and its comparison against main preserves that scope.
Production shutdown intentionally retains provider work: callProvider tracks the underlying queued operation independently of its timeout; stop waits until activeOperations empties. Releasing the test-owned held mock addresses the fixture lifetime without weakening production shutdown.
Existing cancellation helper reused: racePromiseWithAbortSignal rejects its returned promise on cancellation and removes its listener after settlement. Existing helper tests cover source settlement, cancellation, and listener-registration races.
Findings None None.
Security None None.

How this fits together

Gateway worker tests exercise cloud-worker creation and teardown through fake providers and a real service instance. Their shared fixture must drain pending operations before closing its database and allowing subsequent tests to run.

flowchart TD
  A[Deadline test cases] --> B[Worker environment service]
  B --> C[Held provider mock]
  D[Test cancellation or explicit release] --> C
  C --> E[Provider operation settles]
  E --> F[Fixture shutdown and database cleanup]
  G[Restore real timers] --> F
Loading

Before merge

  • Resolve merge risk (P1) - The initiating CI stall remains unexplained; the stated evidence supports containing its test-lifetime consequences.
  • Resolve merge risk (P1) - Reviewer-side dependency inspection remains incomplete for Vitest 4.1.11 cancellation and timer-aware waiting because local source and network access were unavailable.
  • Complete next step (P2) - Complete direct inspection of Vitest 4.1.11 test-context cancellation and fake-timer wait behavior before landing; this review could not access the dependency source.
Agent review details

Security

None.

PR surface

Tests +7. Total +7 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 2 15 8 +7
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 15 8 +7

Review metrics

Metric Value Why it matters
Production versus test growth Production 0; tests net +6; test support net +1 All growth supports fixture cancellation and cleanup; production behavior and existing assertions remain unchanged.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Keep provider-drain guarantees intact while making held test fixtures cancellable and their shutdown independent of fake timers.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Keep provider-drain guarantees intact while making held test fixtures cancellable and their shutdown independent of fake timers.

Do we have a high-confidence way to reproduce the issue?

Unclear for the initiating CI stall. Source establishes the held-provider/shutdown wait cycle, and the PR reports synthetic reproduction; no reproduction was executed during this read-only review.

Is this the best way to solve the issue?

Acceptable mitigation: the patch repairs test-owned lifetimes without weakening production shutdown or deadline assertions. Final best-fix verification still needs direct confirmation of the pinned Vitest contract.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 4cb260cd132f.

Labels

Label changes:

  • add P2: This addresses repeated gateway test-shard hangs with a bounded test-infrastructure repair.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The supplied MEMBER classification exempts this test-only PR from contributor runtime proof; its reported fault probes and shard replay address fixture behavior without claiming a production fix.

Label justifications:

  • P2: This addresses repeated gateway test-shard hangs with a bounded test-infrastructure repair.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: The supplied MEMBER classification exempts this test-only PR from contributor runtime proof; its reported fault probes and shard replay address fixture behavior without claiming a production fix.

Evidence

What I checked:

Likely related people:

  • Peter Steinberger: Raw commit c753bbd adds src/gateway/worker-environments/provider-destroy-timeout.test.ts:18 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: c753bbd1ced5; files: src/gateway/worker-environments/provider-destroy-timeout.test.ts)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete

steipete commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Maintainer disposition for the three open items (head bdfedd8):

Vitest contract verification (the two dependency-inspection items): verified directly against the installed vitest 4.1.11.

  • it.for supplies the test context (including signal) to table callbacks; it.each does not — confirmed in the vitest runner source (chunk-artifact.js, table-context path) and exercised empirically below.
  • The context signal aborts on watchdog timeout/cancellation. Proven by controlled experiment, not just source reading: with the test artificially suspended after provider readiness, the original file wedged the afterEach hook (provider held, fake timers installed); the patched file released the mock via the aborted signal and hooks completed. Logs are in the investigation notes.
  • waitForFast is the repo's existing helper: vi.waitFor(cb, { interval: 1 }), which advances the fake clock between polls. Also proven empirically: a zero-delay timer injected before mock readiness deadlocks the original await started.promise and passes with waitForFast.

The initiating stall remains unexplained: accepted and stated as such in the PR body. The patch converts the two mechanisms that turn a slow turn into a shard-wedging test+hook timeout pair; it does not claim to eliminate the original stall. If main CI stalls again, the recorded follow-up is phase-boundary instrumentation in the non-isolated worker. Containment with intact assertions is the right landing unit here — the alternative is leaving a known shard-wedger live on main while chasing an unreproduced trigger.

CI is green at bdfedd8; the full gateway-core shard file set also passed on a Linux Testbox replay (3,397 tests).

@steipete
steipete merged commit 89adb23 into main Sep 5, 2026
107 checks passed
@steipete
steipete deleted the fix/flaky-ui-e2e branch September 5, 2026 13:11
@steipete

steipete commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Landed as 89adb23. How this was tested:

  • Synthetic fault injection (the CI signature reproduced on demand): a zero-delay timer inserted before mock readiness deadlocks the original test and passes with the fix; a suspended-cancellation probe wedged the original afterEach hook and unwinds cleanly with the patch.
  • Local loop: 30/30 file invocations (300 cases), five under extra CPU load.
  • Linux CI-shard replay on Blacksmith Testbox: the exact gateway-core-3 file set, 182 files / 3,397 tests green.
  • CI: changed-file shard checks-node-changed-1 ran the fixed tests green; full PR checks green at bdfedd8.
  • Reviews: ClawSweeper found no concrete defect (maintainer disposition posted for its environment-limited verification items); independent autoreview found no P0–P2 findings.

Known limit, stated on the PR: the initiating stall was not naturally reproduced — this patch hardens the two mechanisms that produced the observed test-timeout + hook-timeout signature. If main stalls again, the follow-up is phase-boundary instrumentation in the non-isolated worker.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 6, 2026
…hard (openclaw#139090)

The five-minute checkpointing tests held the provider mock on an
unresolved promise and awaited mock readiness without advancing fake
timers. A watchdog timeout then left the provider held, and afterEach
joined it via service.stop() while fake timers were still installed,
turning one slow test into a test-timeout plus hook-timeout pair
(seen failing repeatedly on main CI).

Await mock readiness through the existing timer-aware waitForFast, race
held mocks against the test cancellation signal, and restore real timers
before draining operations and before service.stop() in the shared
fixture. Assertions and production code are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime maintainer Maintainer-authored PR P2 Normal backlog priority with limited blast radius. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant