Skip to content

test(handoff): bound interrupt pipe reads to prevent indefinite CI hangs #370

Open

Description

@abruption

Observed CI stall

PR #369, head e5f9135e50d8df034224d7743337df7248fd88d3, run https://github.com/abruption/session-peer/actions/runs/41546740854 attempt1: macOS/Python3.9 remained in Core tests. The last completed test was test_real_ledger_is_unchanged_and_unchanged_samples_are_suppressed at2026-11-02T23:38:38Z; output stopped until cancellation at2026-11-03T00:01:34Z. Normal cancellation did not end the job promptly; force-cancel completed it.

The next alphabetical test is HandoffWatch.test_real_pipe_never_changes_flags_and_reaps_on_interrupt_or_broken_pipe. This location is a candidate, not proof of the original runner stack or scheduling cause.

Targeted rerun of only that matrix job and its dependencies passed at the same head in attempt2 (2026-11-03T00:02:33Z–00:04:34Z). All43 PR checks passed afterward. Successful rerun does not establish the original cause or fix the test hazard.

Independently reproduced fixture hazard

At tests/core/test_handoff_watch.py, the interrupt=True arm calls os.read(read_fd,4096) without readiness/deadline protection while the parent still owns the pipe write FD.

The runtime may preserve interrupt exit130 even when its final end-frame write reports output_unavailable. If no frame reached stdout, helper cleanup does not create EOF because the parent stream still holds the writer. The fixture then waits indefinitely instead of failing.

A bounded local macOS/Apple Python3.9.6 subprocess demonstrated this condition without changing source files:

  • Five unmodified isolated executions passed (about0.14–0.27s each).
  • Inject only interrupted end-frame HandoffWatchOutput.write returning the actual error enum output_unavailable; delegate other writes to the original implementation.
  • Run the existing test with faulthandler.dump_traceback_later(2) and an external5s watchdog.
  • The watchdog expired; traceback showed the main thread at the fixture's os.read (line325 at this head).

This fault injection demonstrates a real unbounded fixture read. It is not a replay of the CI environment, proof that startup delay triggered attempt1, or evidence that the product watch ignores its own deadlines. An earlier broad mock returning False was an invalid injection (False is not a write-error enum) and did not demonstrate this hazard; it is excluded from the evidence above.

Proposed acceptance

  • Bound every read in this fixture with readiness/deadline observation. Missing output must fail promptly with the existing bounded/sanitized output diagnostics, not hang or silently pass.
  • Preserve the successful end-frame content, inherited-FD flags and owned-helper reaping assertions. Do not solve this by increasing sleeps or weakening assertions.
  • Add a negative regression for a legitimate final-output failure and retain a synchronized real-output positive control, separating interrupt exit/reaping from short scheduler/output-budget guarantees.
  • Inspect adjacent real-pipe fixtures for the same observation pattern.
  • Consider separately bounded job/step timeouts or traceback diagnostics so another hang cannot consume the entire Actions default allowance; do not substitute a timeout for repairing the fixture.
  • Recheck macOS Python3.9 and relevant POSIX tests. Keep actual model/native/SSH delivery out of this test work.

No runtime regression is established and no runtime version bump/publication is authorized by this issue. The docs-only PR #369 need not absorb this separate test-maintenance change.

Activity

  1. self-assigned this
    on Nov 3, 2026
  2. added
    bugSomething isn't working
    priority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.
    on Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpriority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions