Skip to content

ui-server: dirty failed mount case intermittently counts a seventh native socket #3550

Closed

Description

@schlessera

Context

packages/ui-server/tests/http-supported-mounts.test.ts "a dirty failed mount discards its resources before the next independent case" intermittently counts seven native-manager client sockets where it expects six (expect(sockets).toHaveLength(6), packages/ui-server/tests/http-supported-mounts.test.ts:286 on main @ 0760f7832).

Observed 2026-11-04:

The case opens six native.ready() calls and then counts the sockets in the fresh fixture's agent bucket. Something else in the fresh app is making a native call while those six connect and holds one more socket. #3342's commit 5e8939bce added await fresh.t.app.wsHost.journal?.maintain() before the six calls, on the theory that the turn journal's startup idle pass (#3386) was that caller. With that await in place, the case still fails, so the journal pass is not, or not the only, background caller.

#2894 (closed) was this case's earlier timing problem, which was six leftover idle timers after disposal. That is a different assertion and cause; its socket-key and join fix is still in place.

Scope

Find which native-manager calls a freshly prepared app makes in the background after prepare() returns. Record the calling site and when it runs relative to the six ready() calls in a failing invocation. Then make the case wait for that work, or count only the sockets its own six calls opened, whichever matches the case's intent: native shutdown cancels every socket idle deadline.

Out of scope

  • Retries or raised deadlines.
  • Loosening the assertion to "at least six".
  • Changing the product's startup behaviour only to make the test pass. If the background call is itself a defect, file it separately.

Acceptance criteria

  • The extra socket's caller is named, with a file:line citation, from a failing invocation that is captured on purpose (for example a forced delay).
  • The case passes 20 consecutive isolated runs on main, plus the full ui-server suite.
  • A mutation receipt shows the case still fails if native shutdown leaves a socket idle deadline pending.

Verification

bun test --timeout 30000 packages/ui-server/tests/http-supported-mounts.test.ts -t 'dirty failed mount' repeated, then bun run test in packages/ui-server. This needs a host with unprivileged user namespaces for the rest of the file.

Contract impact

None expected; test fixture synchronization only.

Activity

  1. added
    type: testCoverage, harness or proof work with no behaviour change.
    area: ui-serverHono app factory: turn coordinator, auth, session catalog, routes, cron.
    priority: p1Scheduled into the current or next milestone.
    on Nov 4, 2026
  2. added this to the 0.45.0 milestone on Nov 4, 2026
  3. added
    agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.
    on Nov 4, 2026
  4. self-assigned this
    on Nov 4, 2026
  5. schlessera commented on Nov 4, 2026

    @schlessera
    OwnerAuthor

    Claiming (token claim-3550-a7f3). Branch: test/3550-dirty-mount-seventh-socket from main @ 2fa67923d.

    Path collision noted: #3342 also edits packages/ui-server/tests/http-supported-mounts.test.ts (it is the PR this issue unblocks); other reports are closure-level on ui-server.

  6. schlessera commented on Nov 4, 2026

    @schlessera
    OwnerAuthor

    Rate comparison, 2026-11-04: does #3342 or its commit 5e8939bce raise the rate?

    I ran only this case 20 times on each of three trees: bun test --timeout 30000 tests/http-supported-mounts.test.ts -t 'dirty failed mount' in packages/ui-server. The trees ran interleaved (main, head, head without the await, then repeat), one run at a time on one host, so all three saw the same load. Every run either passed or failed with Expected length: 6 / Received length: 7. No run failed in any other way.

    Tree Failed
    main @ 0760f7832f3d81f897f7925b417e2a53dd2465b8 (#3342's base) 10 / 20
    #3342 head d73637092bdd7a76ac43e07f2fd824405183f9ca (includes 5e8939bce) 8 / 20
    #3342 head with 5e8939bce reverted (no await fresh.t.app.wsHost.journal?.maintain()) 11 / 20

    Conclusion: neither #3342 nor 5e8939bce raises the rate. The three counts are within sampling noise at n = 20. The await before the six ready() calls does not measurably lower it either, so the turn journal's startup idle pass is at most one source of the seventh socket. Most of it comes from something else, and that something is present on plain main.

    The rate on this host today (about 50%) is much higher than in the first five-run samples (1 of 5 on main). Run-to-run load clearly matters, so compare arms only within one interleaved batch.

  7. schlessera commented on Nov 4, 2026

    @schlessera
    OwnerAuthor

    Status (2026-11-04): #3552 implements this and is ready at head 35313bdfa; it is not merged.

  8. removed their assignment
    on Nov 4, 2026
  9. added 2 commits that reference this issue on Nov 4, 2026
    00d204a
    953c459
  10. added a commit that references this issue on Nov 4, 2026
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

    agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.area: ui-serverHono app factory: turn coordinator, auth, session catalog, routes, cron.priority: p1Scheduled into the current or next milestone.type: testCoverage, harness or proof work with no behaviour change.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions