Skip to content

Windows ticket-sharing EPERM rejects Kibitzer wake waiters and leaks test work #8953

Description

@code-yeongyu

Summary

The focused Windows Kibitzer wake-slot soak intermittently rejects queued wake admissions while another waiter is transitioning its FIFO ticket. The failing test then exits without cancelling the remaining waiters, so their later rejections leak into unrelated memory tests.

Reproduction

  1. Dispatch .github/workflows/windows-flake-soak.yml on Windows with target=paths.
  2. Set paths=packages/omo-senpi/src/components/memory/kibitzer/wake-slot.test.ts and iterations=10.
  3. Observe an intermittent failure in the FIFO test.

Focused reproduction: https://github.com/code-yeongyu/oh-my-openagent/actions/runs/36320480068 (iteration 6).

Expected

  • A transient Windows sharing violation while reading the current head ticket keeps that ticket in place and retries through the existing bounded poll path.
  • Queue waiters do not reject for expected EPERM/EBUSY/EACCES sharing states.
  • FIFO order and the configured maximum concurrent wake count remain unchanged.
  • The FIFO test cancels and joins every waiter and releases every acquired lease even when an assertion fails.
  • Three focused Windows soaks of 10 iterations each pass.

Actual

The third waiter rejects in reapDeadHead, while the test reports only that the promise was already settled:

EPERM: operation not permitted, open ...recall-wake.tickets/<ticket>
  at retryOnEintr (.../packages/memory-core/src/fs/retry.ts:11)
  at reapDeadHead (.../packages/memory-core/src/locks/recall-wake-domain.ts:108)
  at acquireRecallWakeLease (.../packages/memory-core/src/locks/recall-wake-domain.ts:179)
  at acquire (.../packages/omo-senpi/src/components/memory/kibitzer/wake-slot.ts:62)

Expected: false
Received: true
  at .../wake-slot.test.ts:81

After the assertion exits, pending admissions continue running. Runs https://github.com/code-yeongyu/oh-my-openagent/actions/runs/36312352680 and https://github.com/code-yeongyu/oh-my-openagent/actions/runs/36300728869 later report expected an acquired admission, got busy with stacks back into wake-slot.test.ts, including an unhandled error between tests.

Evidence

  • packages/memory-core/src/locks/recall-wake-domain.ts:104-112: reapDeadHead treats only ENOENT as a moved ticket. Its documented contract says unreadable or unparsable tickets keep their place, but Windows sharing errors currently propagate.
  • packages/omo-senpi/src/components/memory/kibitzer/wake-slot.test.ts:24-28: settledAlready deliberately maps both fulfillment and rejection to settled, hiding the EPERM cause behind a pending-state assertion.
  • The focused macOS stress run passed 300/300 adapter tests, and the direct memory-core stress run passed 450/450 tests. The failing condition is the Windows file-sharing path.

Root cause

Confirmed product defect: Windows can return EPERM, EBUSY, or EACCES while another waiter is publishing or withdrawing a ticket. reapDeadHead propagates that expected sharing state instead of retaining the head ticket and waiting for the next normal poll. The apparent FIFO failure is a rejected waiter, not evidence that more than two leases were live.

Confirmed test defect: the FIFO test does not abort and join remaining waits or release every acquired lease when an assertion fails, allowing asynchronous work to contaminate later tests.

Scope

  • Fix ticket-head reads to fail closed on Windows sharing errors without adding sleeps, retries outside the existing poll loop, timeout increases, or platform skips.
  • Add deterministic coverage for the sharing-error branch.
  • Make the FIFO test teardown unconditional and event/state based.

Related

Activity

  1. code-yeongyu commented on Sep 27, 2026

    @code-yeongyu
    OwnerAuthor

    Root cause confirmed from focused Windows soak https://github.com/code-yeongyu/oh-my-openagent/actions/runs/36320480068 (iteration 6): reapDeadHead received EPERM while opening the live head ticket and propagated it. The FIFO assertion only reported that the waiter had settled because its helper collapsed fulfillment and rejection into one state. No evidence showed capacity above two.

    RED: bun test packages/memory-core/src/locks/recall-wake-domain.test.ts -t "transient sharing violation" failed with Expected: "acquired" / Received: "rejected".

    GREEN after the minimal product change: the same command passed 1 test, 0 failed. The fix retains an unreadable Windows ticket as queue head and returns to the existing bounded poll path. Both FIFO tests now abort/join every waiter and release every acquired lease in finally, preventing the observed cross-test cascade.

  2. code-yeongyu commented on Sep 28, 2026

    @code-yeongyu
    OwnerAuthor

    Fixed by #8961 (merge commit 6f65ada): kibitzer wake-slot: a Windows sharing violation on the head ticket read is treated as contention (the ticket stays head) instead of rejecting the waiter.

    Evidence: 3 focused windows-latest soaks green before merge (in the PR); the combined fix set was full-matrix green 3 times on the integration head; after merge, dev push CI run 36360065018 on e89647d (which contains every win-ci fix) was green in all 3 attempts on every Windows job, with the test steps actually executed.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions