Skip to content

Windows ca-pi adapter suites flake on hard 5s timing budgets #428

Description

@SUaDtL

Symptom

The [CHECK] | [PI] | Adapter contract <os: windows-latest ...> cells intermittently fail on timing, not logic. Two distinct instances observed during the 2026-07-24 backlog sweep:

  1. test/background-jobs.test.ts > session-local background job state > launches and disposes a real Git Bash background job

    Error: Test timed out in 5000ms.
    × launches and disposes a real Git Bash background job  5015ms
    

    Missed the budget by 15ms. Observed on PR fix(ca-pi): revoke mutator authority on trust withdrawal and correlate bridge failures #427, windows-latest / Pi 0.80.10. The other five matrix cells — including windows-latest / Pi 0.80.5, i.e. the same OS and the same code — all passed. The Pi runtime version is irrelevant to spawning Git Bash, so this is runner contention, not a behavioral difference.

  2. test/process-tree.test.ts — Windows Job Object holder refused containment: ready-timeout, after the test's own built-in retry. Observed on run 30130392781, windows-latest / Pi 0.80.5.

Why it matters

These are required merge-gate cells, so a flake blocks the PR and the cascading [GATE] | [REPO] | Merge readiness with it. It already cost one re-run this sweep and will keep taxing every ca-pi PR. Worse, a real Windows regression is now indistinguishable at a glance from noise — which is exactly how a genuine defect gets waved through as "just the flake".

Root cause

Both tests spawn real OS processes (Git Bash; a Job Object holder) and assert against a fixed wall-clock budget. Windows GitHub runners are slower and noisier at process creation than Linux/macOS, so a budget tuned on a developer machine sits right at the edge.

Suggested direction

Not simply "raise the timeout" — that trades a flake for a slow suite and hides real regressions. Prefer, in order:

  1. Make the wait condition-based rather than duration-based where possible: poll for the observable readiness signal with a generous ceiling, so a fast runner stays fast and a slow one still passes.
  2. Where a hard bound is genuinely part of the contract, give the Windows path an explicitly platform-scoped budget with a comment stating the measured baseline it derives from — not an arbitrary round number.
  3. If a test's purpose is the containment behavior rather than its latency, decouple the assertion from wall-clock entirely.

Acceptance criteria

  • AC-1: Neither test asserts a bare fixed millisecond budget against real process spawn on Windows.
  • AC-2: A deliberately slowed spawn still passes, while a genuinely hung one still fails within a bounded time and leaves a useful diagnostic.
  • AC-3: The chosen budgets cite the observed baseline they came from.

Related: #399 added explicit job deadlines; this is the test-level counterpart.

Activity

  1. SUaDtL commented on Jul 25, 2026

    @SUaDtL
    CollaboratorAuthor

    Third occurrence, now blocking PR #426.

    × launches and disposes a real Git Bash background job  5007ms
    Error: Test timed out in 5000ms.
    

    windows-latest / Pi 0.80.5, run 30138399363. Missed the budget by 7ms; the other five matrix cells passed the identical code.

    Tally so far: PR #427 (windows-latest / Pi 0.80.10, 5015ms), PR #426 (windows-latest / Pi 0.80.5, 5007ms), plus the process-tree.test.ts ready-timeout instance on run 30130392781. Every one has needed a manual re-run of a required merge gate.

    The margins — 7ms and 15ms over a 5000ms budget — confirm this is runner contention at the threshold, not a behavioural difference. Raising the number would only move the cliff; the fix is to make the wait condition-based, per the ACs above.

    Priority note: the cost is no longer just wasted re-runs. Three false reds in one day trains reviewers to reflex-rerun a red Windows cell, which is exactly how a genuine Windows regression gets waved through.

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

    sev:medTribunal/triage: medium severity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions