Skip to content

[CI] Flaky test isolation: 'Unbound layer node' leaks into unrelated test failure during --only-failures rerun #181

Description

@alltomatos

What to build

Root-cause and fix a test-isolation bug in packages/opencode's CI test run, observed while chasing unrelated CI failures on #180 (a pure-CSS PR that cannot be the cause).

Observed behavior

unit (linux)/unit (windows) CI jobs run bun test twice via turbo: a full pass first, then bun test --timeout 60000 --only-failures to retry anything that failed. In several CI runs, the second (--only-failures, presumably a smaller/isolated subset of files) invocation shows:

test/session/snapshot-tool-race.test.ts:

# Unhandled error between tests
-------------------------------
...
error: Unbound layer node: @opencode/example/LocationServiceMap
      at .../packages/core/src/effect/layer-node.ts:260:48
      at .../packages/opencode/test/session/snapshot-tool-race.test.ts:96:13
-------------------------------

test/session/prompt.test.ts:
...
InterruptError: All fibers interrupted without error
...
error: All fibers interrupted without error
      at causeSquash (.../[email protected]/.../effect.js:171:12)
(fail) loop includes MCP instructions in model system context [20635.94ms]

snapshot-tool-race.test.ts:96 calls LayerNode.compile(root, [...]) (see the file for the exact root/replacements). packages/core/src/effect/layer-node.ts's compile() builds a fresh cache per call (not a shared/module-level cache), so the "Unbound layer node" isn't a cross-test cache collision — it means the root graph in snapshot-tool-race.test.ts genuinely resolves a dependency chain that reaches a LocationServiceMap node with kind: "unbound" that isn't satisfied by that file's root/replacements list. This only reproduces in the --only-failures isolated rerun, not the full run (1121 pass / 0 fail beforehand) — i.e. it's order/isolation-dependent, not deterministic.

The resulting unhandled async error appears to interrupt whatever test the runtime happened to be executing next (prompt.test.ts's "loop includes MCP instructions in model system context", which forks a fiber via prompt.loop(...).pipe(Effect.forkChild) and later calls yield* Fiber.interrupt(fiber) without awaiting/ignoring its exit — see line ~589). This test already has a comment documenting prior CI-load-related flakiness (bumped from 15s to 40s timeout, 2026-09-08 investigation) but this specific InterruptError failure mode is new/different (finishes at ~20s, well under the 40s budget — not a bare timeout).

Investigation needed

  1. Confirm why snapshot-tool-race.test.ts's root graph is missing a binding for @opencode/example/LocationServiceMap only when run via --only-failures isolation (does something else register that node as a side effect during the full run, that isn't present when the file runs in isolation?).
  2. Confirm/rule out whether the unhandled error genuinely crosses file boundaries inside the same bun-test worker process, interrupting prompt.test.ts's forked fiber — or if this is coincidental timing in the log and the two failures are unrelated.
  3. If (2) is confirmed, either fix snapshot-tool-race.test.ts's layer graph so it never produces an unbound-node error, and/or harden prompt.test.ts:589's Fiber.interrupt(fiber) call (e.g. Fiber.interrupt(fiber).pipe(Effect.ignore), matching the fact the test doesn't care about that fiber's exit) so an unrelated interrupt elsewhere can't fail it.

Acceptance criteria

  • bun test --timeout 60000 --only-failures run of packages/opencode no longer produces the "Unbound layer node: @opencode/example/LocationServiceMap" error from snapshot-tool-race.test.ts
  • "loop includes MCP instructions in model system context" in prompt.test.ts no longer fails with InterruptError: All fibers interrupted without error under repeated CI runs
  • Root cause documented in the PR (whether the two failures were actually linked, or coincidental)

Blocked by

(none)

Verification

  • Re-run unit (linux)/unit (windows) CI jobs on a few consecutive PRs and confirm the specific errors above no longer appear (some general CI flakiness may remain — only this specific pair of errors is in scope)

Activity

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

    bugSomething isn't workingtech-debtRefactors, cleanup, architectural debt

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions