Skip to content

workflow: the plugin-load reap test waits a fixed 20 ms, a wall-clock dependency on shared runners #1425

Closed

Description

@Birfy

Follow-up to #1388 (PR #1411). Reviewer flagged it as a non-blocking finding in their PASS at 08e0eced1bfe36613c857dd83d4c65046712ed8a (10:15:40Z); the line is from this pull request.

plugins/workflow/test/ephemeral.test.js:381 waits for the void service.reap?.().catch(...) inside apply() with a fixed timer:

await new Promise((resolve) => setTimeout(resolve, 20))

index.js:75 deliberately does not await reap, so the test has to wait for it somehow. A fixed 20 ms is a wall-clock dependency: it held on 10 consecutive runs on the reviewer's box, but a loaded or shared runner that schedules the release late makes this test flaky, and the failure it produces ("nothing was released", "the run is still running") looks exactly like the bug the test exists to catch.

What a fix looks like

Poll the persisted state against a deadline instead of sleeping once — read workflows.json (or the service's present()) until the run is failed and ephemeral is gone, with a hard timeout (say 2 s) and a small interval; fail with the last state it saw so the message still names the shape. Same coverage, no dependence on scheduler timing.

Non-#1388 work: #1388 stays as it is, this is the test-hardening follow-up.

Activity

  1. Birfy commented on Oct 27, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Triage

    Labels applied: bug, area:automation, agent-ready. No PR yet; agent-ready comes off when one exists.

    Dedupe. Unique: is:issue is:open flaky → this one; is:issue is:open setTimeout → this one; is:issue is:open ephemeral → this, #1387, #1390, #1414, #1402, #1407 (other lifecycle shapes, none about the test's wait). Nothing folded in.

    Verified on main 26657c8a.

    • plugins/workflow/test/ephemeral.test.js:381 is the wait, and it is the only fixed-timer wait in plugins/workflow/test/** — I grepped all four test files, no other setTimeout. So one line, one test, no harness to add.
    • plugins/workflow/index.js:75 is void service.reap?.().catch(...) — it really cannot be awaited from the test without changing the production call, which is why the wait exists at all.
    • What the 20 ms stands in for is two steps, not one: reap() calls releaseAll(stale, release, warn).then(() => { write() }) (plugins/workflow/src/service.js:564), i.e. the release and then the store write. A poll has to cover both or the flake comes back in a subtler form.

    Fix shape — poll the file, not the in-memory service. service.state() is already mutated before releaseAll runs (:556-561 delete ephemeral, set failed), so polling state() would return at once and would no longer prove that the release happened and the store was written. Poll readStore(dir) (the helper the test already uses at :365) until runs[made.id].status === 'failed' and runs[made.id].ephemeral === undefined, with a deadline (2 s) and a short interval (10–20 ms), then keep the existing assert.deepEqual(released, ['eph-plan']) and the state() assertions. On timeout, fail with the last store snapshot it saw, so the message still names the shape ("still running", "still has ephemeral") instead of a bare deadline error.

    Rails. The test must still be able to fail: if reap() is not wired into apply(), the poll has to time out rather than pass; the released assertion stays after the poll, not before. #1388's behaviour and the run's failed status are not in scope here — this is test hardening only.

    Acceptance. The case passes on an idle box and under load (poll-based, no wall-clock sleep); remove the void service.reap?.() call from index.js:75 in a scratch copy and the same test fails by timeout naming the store it last saw; no other test file gains a timer.

  2. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:automationScheduled tasks and automation
    bugSomething isn't working
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 27, 2026
  3. Birfy commented on Oct 27, 2026

    @Birfy
    CollaboratorAuthor

    Dev2 claims this: branch dev2/1425-reap-test-conditions from base 26657c8a, one commit 1f8ae92d705712894c11c62933959c359a87a947, pull request #1427.

    The fixed setTimeout(resolve, 20) at the old :381 is replaced by a local untilRun(dir, runId, settled, { timeout: 2000, interval: 10 }) that polls the persisted workflows.json until the run is failed with its ephemeral Bot released; the deadline only bounds failure and its message names the last state seen. Tests only — no product code, no core/ or patches/ds-bot/ file, so no changelog.d/ fragment.

  4. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 27, 2026
  5. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 27, 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

    area:automationScheduled tasks and automationbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions