Skip to content

tests: unit shard intermittently fails subscription-status, turn-scope and answer-delivery liveness together #3256

Closed

Description

@schlessera

Context

Observed on PR #3245, head b80acd2a1624d16e532e8750b874d5cc272cfaf6, base a84d9bfbcb4e608b504aa0a9feb1144ad9a0b77e: the unit (3) shard failed (check run 125037245269, started 2026-11-02T00:54:46Z) with 16 failures in ui-server and ui-react tests. The same shard passed on the previous head 830dbfbdc367efd674e20cdf7bf9b02d6fa0c7d7 about 45 minutes earlier. The two trees differ only in docs/integration-contract.md (prose only), so the PR's change cannot explain the failures.

Failing tests in that shard:

  • packages/ui-server/tests/subscription-status.test.ts: all six /api/status's subscription tests that need a completed turn. Example: subscription-status.test.ts:307 expected provenBy "turn" and received null, so the turn did not record as a successful subscription turn.
  • packages/ui-server/tests/turn-scope.test.ts: freeze/resume and storage-accounting tests, for example turn-scope.test.ts:41 and :73 (toMatchObject mismatches) and IGNORED_BYTES_ONCE_PER_PATH_SCOPE_COUNTER.
  • packages/ui-react/tests/answer-delivery-runtime.test.ts:976: "Liveness B: a blackholed idle socket is probed after 15 s…", which failed with Liveness native event did not complete.

Locally, subscription-status.test.ts passes. In a sandbox without the native tmpfs runtime, some turn-scope.test.ts cases fail on both this branch and main. That is not the CI symptom.

Related, but not the same symptom: #2551 (low-space failures in running-turn fixtures) and #2989 (answer-delivery storage refusal).

Scope

Scoped on 2026-11-02 (see Acceptance criteria). Original note: needs scoping. First establish whether these tests fail together under runner load or resource pressure (shared worker directories under .brain-unit-workers-*, disk, timing). Then establish whether one shared fixture or harness cause explains all three files.

Out of scope

Raising timeouts or retrying to get green without a diagnosed cause.

Acceptance criteria

Attributed on 2026-11-02 from the second occurrence. Hosted run 41505209464, unit (1), had 2,665,242,624 bytes free on the root filesystem that holds the fixture brains. The turn floor max(5% of 76.9 GB, 2 GiB) is 3,844,357,734.4 bytes, so turns stopped at the floor. A preload that fakes those statfs figures reproduces the CI failure lines on an unchanged base.

  • Hosted unit shards begin proof with free space on the fixtures' filesystem of at least the turn-fixture floor plus the proof's own consumption. This is a provisioning fix: it reclaims or measures what fills the runner. It does not change the floor formula.
  • A runner that cannot provide that headroom fails in environment setup, as a named infrastructure prerequisite, before any test runs.
  • Each proof records the fixtures' filesystem (total, free at start, minimum, free at end, floor). A dip below the floor produces a named annotation that attributes turn-dependent failures to runner disk exhaustion.
  • The floor's protective purpose is unchanged. The production check and requireTurnFreeSpace keep max(5%, SCOPE_FREE_BYTES), and a test pins the CI copy of the formula to it.
  • Failing-first and mutation receipts are recorded. Off-main qualification is recorded on the PR (proof-system lane): hosted unit shards with the measured runner disk, none of which reaches the floor.
  • Coordination with tests: attribute low-space failures in remaining running-turn fixtures #2551. Per-fixture requireTurnFreeSpace calls in the remaining suites, including subscription-status.test.ts, stay in tests: attribute low-space failures in remaining running-turn fixtures #2551's scope. This issue covers the runner and the job-level attribution.

The first occurrence's turn-scope.test.ts and answer-delivery liveness symptoms have no disk figures. If they recur on a runner with recorded headroom above the floor, they get a new issue.

Contract impact

None expected.

Triage notes (2026-11-02, main 3b9f3e84b)

No local reproduction was attempted in triage. The single natural receipt above, from a shard that passed on the neighbouring head with a prose-only tree difference, is the only evidence. It establishes the failure but not its cause.

What would complete the reproduction. Any one of these would do:

  • A second natural occurrence with the shard's runner resource figures (free disk and inode counts in the worker and .brain-unit-workers-* directories, and load).
  • A controlled local run that pins the three files to one worker under constrained disk or CPU and reproduces at least two of the three symptoms together.
  • A per-file run showing that the symptoms are independent.

The provenBy: null at subscription-status.test.ts:307 is the most specific symptom. Establishing why the completed turn did not record as a subscription turn is the first narrowing step.

Acceptance once attributed. A named cause per symptom, or one shared cause shown to explain all three. A fixture or harness repair scoped to that cause, with a controlled failing-first receipt. No raised deadline or retry.

Labels: added priority: p2 and needs: repro. Left standalone. #736, which owns non-browser verification determinism, is at GitHub's 100 direct sub-issue limit, so this is related to it by reference only. #2753 covers only browser runs.

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.
    area: ui-reactReact chat/files/voice components, stores, WebSocket transport.
    priority: p2Wanted, not scheduled. Pulled in when a milestone has room.
    needs: reproBlocked on a reproduction.
    on Nov 2, 2026
  2. schlessera commented on Nov 2, 2026

    @schlessera
    OwnerAuthor

    Second natural occurrence, with resource figures and a controlled reproduction (2026-11-02)

    Natural occurrence. CI run 41505209464, job unit (1) (shard 1/4), PR #3342 head b2c0cbcdd, base 4cd198552 (merge checkout 475bd80bb). All six /api/status's subscription turn tests failed exactly as described above, at subscription-status.test.ts:307, :321, :345, :363, :501 and :539. Two other failures in the same worker named the cause directly:

    • packages/ui-react/tests/connection-continuity-runtime.test.ts:215: TurnFixtureFreeSpaceError, with 2,665,242,624 bytes available and at least 3,844,357,734.4 required (max(5% of total, 2 GiB)).
    • packages/ui-server/tests/session-taint.test.ts:90: the turn journal carries a free_space_floor receipt (count 2,618,183,680, cap 3,844,357,734.4, state stopped).

    Worker 2's html-preview-runtime.test.ts failures came afterwards. The runner SIGKILLed worker 2 (exit 137) after worker 1 failed.

    Controlled reproduction. A throwaway --preload, not committed, wraps statfsSync. For the test's subscription-app-* brains it returns the CI figures above, about 76.9 GB total and 2,618,183,680 bytes free. Everything else stays real.

    • On the epic base 4cd198552, bun test tests/subscription-status.test.ts with the preload fails the same tests at :307, :321, :345, :363 and :501, each with provenBy/lastProvenAt null. Without the preload it passes 32/32.
    • On the PR head, the same preload fails all six, at the same six lines as CI.

    Attribution. The turn scope's free-space floor (check, packages/ui-server/src/brain/turn-scope.ts:94-120) stops the fixture turn before it succeeds, so no successful subscription turn is recorded. This explains the subscription-status symptom. Unlike the continuity fixture, subscription-status.test.ts does not call requireTurnFreeSpace, so it reports a null assertion instead of naming low space. That is the same gap #2551 tracks. This note does not attribute the turn-scope.test.ts and answer-delivery symptoms from the first occurrence.

  3. self-assigned this
    on Nov 2, 2026
  4. schlessera commented on Nov 2, 2026

    @schlessera
    OwnerAuthor

    Claimed (token claim-3256-a7d2). Intended branch: fix/3256-unit-shard-disk-floor, from origin/main 3984040f1. Collision check follows. #2551 is unassigned and has no open PR, so this issue's scope is coordinated with it below rather than taken over.

  5. removed
    priority: p2Wanted, not scheduled. Pulled in when a milestone has room.
    needs: reproBlocked on a reproduction.
    on Nov 2, 2026
  6. added this to the 0.45.0 milestone on Nov 2, 2026
  7. added
    agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.
    priority: p1Scheduled into the current or next milestone.
    on Nov 2, 2026
  8. schlessera commented on Nov 2, 2026

    @schlessera
    OwnerAuthor

    Readiness (2026-11-02)

  9. schlessera commented on Nov 3, 2026

    @schlessera
    OwnerAuthor

    Handoff (2026-11-03): at the orchestrator's direction, PR #3379 is taken over as the declared red-main fix for incident #3427. Main went red at e04768a3f because a unit shard's runner disk fell below the turn floor: 2.67 GB free against 3.84 GB required. I'm refreshing the PR onto current main and landing it through integrate.ts declare-fix / confirm. The earlier claim claim-3256-a7d2 has had no activity since 2026-11-02 21:50Z.

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

Metadata

Metadata

Assignees

Labels

agent-readyContext and acceptance criteria are complete enough for an agent to take this unattended.area: ui-reactReact chat/files/voice components, stores, WebSocket transport.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