fix(background-agent): a parent wake retry no longer waits a second promptAsync hold - #8956
Closed
code-yeongyu wants to merge 3 commits into
Closed
code-yeongyu wants to merge 3 commits into
code-yeongyu wants to merge 3 commits into
Conversation
Owner
Author
|
Probe timing sensitivity noted in QA is tracked as #8958. Final-code Windows soaks: 36322548103, 36322550277, 36322552645 (results to follow). |
Owner
Author
|
Final-code focused Windows soaks (windows-latest, both test files, 10 iterations each): 36322548103 success, 36322550277 success, 36322552645 success (soak head 89e458c = this PR's tree + the throwaway soak workflow commit). |
code-yeongyu
force-pushed
the
fix/win-ci-parent-wake-empty-turn
branch
from
September 27, 2026 13:37
bde63bd to
4ed7ec3
Compare
code-yeongyu
marked this pull request as ready for review
September 27, 2026 15:19
…romptAsync hold (#8951) A parent wake that met its own promptAsync post-dispatch hold always retried after a fixed 2 s, the whole hold length. The hold expires lazily on Date.now(), so a retry timer that fired a tick early (coarse Windows timers) still saw the hold and waited a second full hold. The gate's reserved result now carries the hold's expiresAt. The first meeting with a hold keeps the full 2 s back-off; meeting the same hold again (same expiresAt) waits only what is left of it.
code-yeongyu
force-pushed
the
fix/win-ci-parent-wake-empty-turn
branch
from
September 27, 2026 17:42
69c14b9 to
7d73a7f
Compare
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the Windows-only red in
parent-wake-empty-turn-requeue.test.ts(dev run 36300728869) at its product root, and removes the sleep-pollingwaitUntilfrom that test.Root cause
A parent wake that meets its own promptAsync post-dispatch hold (
DEFAULT_PROMPT_ASYNC_POST_DISPATCH_HOLD_MS = 2_000) was requeued with a fixedscheduleFlush(2_000), the full hold length. The hold expires lazily onDate.now()(reservations.ts). A retry timer armed a few ms after the hold started, firing a tick early on coarse Windows timers, still saw the hold and waited a second full hold (about 4 s). That is needless parent-wake latency in production, and it pushed the test past its 4 s polling window (4440 ms on CI).Fix
utils/prompt-async-gate: thereservedresult carries the hold'sexpiresAt. One helper (reservedDispatchResult) builds it for both reserved paths.parent-wake-prompt-dispatch.ts: the first meeting with a hold keeps the existing 2 s back-off, so first-meeting timing is unchanged. Meeting the same hold again (sameexpiresAt, carried on the wake asgateHoldExpiresAt) waits only the rest of that hold.promptAsynccall, subscribing before the trigger with a bounded timeout. No sleep loop.parent-wake-prompt-dispatch.test.tspins both halves with a frozen clock. The first meeting backs off 2000 ms. A same-hold retry 5 ms before expiry waits 5 ms; on the base code it waits 2000 (RED).expiresAtfield (expect.any(Number)).QA & Evidence
What was tested
bun testonpackages/utils/src/prompt-async-gate*andpackages/omo-opencode/src/features/background-agent: 854 pass, 0 fail.tsgo --noEmit(root and utils) and biome are clean.parent-wake-prompt-dispatch.tsplus the new test gives(fail) ... waits only the rest of that hold(2000 instead of 5)..agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh --expect fixed(opencode serve, fake LLM, plugin loaded from this worktree, isolated XDG), on this branch and on base.What was observed
wakebranch count varies run to run on base and branch alike. A wake turn appears exactly when the dispatched-tracker "no assistant output" requeue fires, which happens when the harness keeps the parent alive more than about 5 s after the noReply admission (seeprobe-attribution.txt). That is a pre-existing sensitivity in the probe's--expect fixedtopology check, unrelated to this change. It is tracked separately.Why it is enough: the only behavior change is limited to re-meeting the same gate hold. It is pinned deterministically RED/GREEN, soaked on Windows, and its absence from the live topology runs is shown by log attribution.
What was omitted: raw logs stay local (
.omo/evidence/20260927-parent-wake-gate-hold-retry/).Fixes #8951
Refs #8324
Summary by cubic
Fixes parent wake retries waiting a second full promptAsync hold, reducing production latency and eliminating a Windows-only test flake.
The promptAsync gate's reserved result now carries the hold's
expiresAt. A wake first meeting a hold keeps the full 2 s back-off; meeting the same hold again waits only the remaining time instead of a second full hold.waitUntilin the empty-turn requeue test with a direct synchronous assertion and a bounded await on the secondpromptAsynccall.expiresAtfield.Fixes #8951.
Written for commit 5751232. Summary will update on new commits.